Skip to content

Commit c567f93

Browse files
committed
Register pool reshard plans in one generation-scoped step
Pool reshard registration now arms the send or receive state first and publishes the plan last under the plan lifecycle lock, carrying a generation like block plans. Sender and receiver settlement, timeout cleanup, and each copy-to-push hand-off act only on their own registration, a failed pool receive drains like any other receive, and a failed pool send retires its uuid, so stale cleanup can no longer drop a reused plan or its transfer progress.
1 parent 7299d73 commit c567f93

3 files changed

Lines changed: 172 additions & 81 deletions

File tree

tpu_sync/core/kv_cache_manager_with_transfer.cc

Lines changed: 130 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,9 +1344,6 @@ absl::Status KVCacheManagerWithTransfer::PoolReshardPush(
13441344
}
13451345

13461346
InitTransportServer();
1347-
TF_RETURN_IF_ERROR(kv_cache::KVCacheManagerBase::RegisterActivePlan(
1348-
plan.uuid(), plan, /*is_sender=*/true));
1349-
13501347
auto state = std::make_shared<PoolReshardSendEntry>();
13511348
state->req_id = plan.req_id();
13521349
state->uuid = plan.uuid();
@@ -1356,13 +1353,30 @@ absl::Status KVCacheManagerWithTransfer::PoolReshardPush(
13561353
state->plan = plan;
13571354
state->deadline = DeadlineFromNow();
13581355
{
1359-
absl::MutexLock lock(mu_);
1360-
if (active_pool_reshard_sends_.contains(plan.uuid())) {
1361-
(void)kv_cache::KVCacheManagerBase::UnregisterActivePlan(plan.uuid());
1362-
return absl::AlreadyExistsError(
1363-
absl::StrCat("pool reshard send UUID already active: ", plan.uuid()));
1356+
// One lifecycle step, like block plans: the send state is armed first
1357+
// and the plan published last under the lifecycle lock, with a
1358+
// generation that scopes every later cleanup to this registration.
1359+
absl::MutexLock lifecycle(plan_lifecycle_mu_);
1360+
if (kv_cache::KVCacheManagerBase::HasActivePlan(plan.uuid())) {
1361+
return absl::AlreadyExistsError(absl::StrCat(
1362+
"Plan with UUID ", plan.uuid(), " is already registered!"));
1363+
}
1364+
state->plan_generation = ++plan_generation_counter_;
1365+
{
1366+
absl::MutexLock lock(mu_);
1367+
if (active_pool_reshard_sends_.contains(plan.uuid())) {
1368+
return absl::AlreadyExistsError(absl::StrCat(
1369+
"pool reshard send UUID already active: ", plan.uuid()));
1370+
}
1371+
active_pool_reshard_sends_[plan.uuid()] = state;
1372+
}
1373+
absl::Status registered = kv_cache::KVCacheManagerBase::RegisterActivePlan(
1374+
plan.uuid(), plan, /*is_sender=*/true, {}, state->plan_generation);
1375+
if (!registered.ok()) {
1376+
absl::MutexLock lock(mu_);
1377+
active_pool_reshard_sends_.erase(plan.uuid());
1378+
return registered;
13641379
}
1365-
active_pool_reshard_sends_[plan.uuid()] = state;
13661380
}
13671381

13681382
// Multi-tag plans scope each pool's staging and pushes to its group's
@@ -1403,14 +1417,16 @@ absl::Status KVCacheManagerWithTransfer::PoolReshardPush(
14031417
// per-peer completion slots instead of failing the plan. The
14041418
// receiver's expected pushes count only senders with scheduled pairs.
14051419
for (size_t peer_idx = 0; peer_idx < peers.size(); ++peer_idx) {
1406-
FinishPoolReshardSend(plan.uuid(), absl::OkStatus());
1420+
FinishPoolReshardSend(plan.uuid(), state->plan_generation,
1421+
absl::OkStatus());
14071422
}
14081423
continue;
14091424
}
14101425
}
14111426
auto future_or = D2hPoolBlocks(pool_idx, pool_src_block_ids);
14121427
if (!future_or.ok()) {
1413-
FinishPoolReshardSend(plan.uuid(), future_or.status());
1428+
FinishPoolReshardSend(plan.uuid(), state->plan_generation,
1429+
future_or.status());
14141430
return future_or.status();
14151431
}
14161432
raiden::PjRtCopyFuture future = std::move(future_or).value();
@@ -1420,11 +1436,12 @@ absl::Status KVCacheManagerWithTransfer::PoolReshardPush(
14201436
++total_outstanding_ops_;
14211437
}
14221438
future.OnReady([this, uuid = static_cast<uint64_t>(plan.uuid()),
1439+
generation = state->plan_generation,
14231440
pool_idx](auto status_or) {
14241441
if (!status_or.ok()) {
1425-
FinishPoolReshardSend(uuid, status_or.status());
1442+
FinishPoolReshardSend(uuid, generation, status_or.status());
14261443
} else {
1427-
StartPoolReshardPush(uuid, pool_idx);
1444+
StartPoolReshardPush(uuid, pool_idx, generation);
14281445
}
14291446
// The callback reads manager state; destruction waits for it.
14301447
absl::MutexLock lock(mu_);
@@ -1435,14 +1452,18 @@ absl::Status KVCacheManagerWithTransfer::PoolReshardPush(
14351452
}
14361453

14371454
void KVCacheManagerWithTransfer::StartPoolReshardPush(uint64_t uuid,
1438-
size_t pool_idx) {
1455+
size_t pool_idx,
1456+
uint64_t generation) {
14391457
std::shared_ptr<PoolReshardSendEntry> state;
14401458
{
14411459
absl::MutexLock lock(mu_);
14421460
auto it = active_pool_reshard_sends_.find(uuid);
14431461
if (it == active_pool_reshard_sends_.end()) return;
14441462
state = it->second;
14451463
}
1464+
// A copy completing for an earlier registration of this uuid must not
1465+
// launch pushes against the current one's plan.
1466+
if (state->plan_generation != generation) return;
14461467

14471468
auto schedule_it = state->plan.shard_push_schedules().find(0);
14481469
if (schedule_it == state->plan.shard_push_schedules().end()) {
@@ -1472,44 +1493,50 @@ void KVCacheManagerWithTransfer::StartPoolReshardPush(uint64_t uuid,
14721493
}
14731494
}
14741495

1475-
transport::BlockTransport* transport_server = nullptr;
14761496
{
1497+
// The pushes are queued while the transport pointer is held under its
1498+
// lock, so a concurrent transport stop cannot destroy it mid-queue.
14771499
absl::MutexLock lock(server_init_mu_);
1478-
transport_server = server_.get();
1479-
}
1480-
if (transport_server == nullptr) {
1481-
FinishPoolReshardSend(
1482-
uuid, absl::FailedPreconditionError("transport server is not running"));
1483-
return;
1484-
}
1485-
1486-
for (const auto& [peer, transfers] : transfers_by_peer) {
1487-
std::vector<int> src_ids;
1488-
std::vector<int> dst_ids;
1489-
src_ids.reserve(transfers.size());
1490-
dst_ids.reserve(transfers.size());
1491-
for (const auto& [src_id, dst_id] : transfers) {
1492-
src_ids.push_back(src_id);
1493-
dst_ids.push_back(dst_id);
1500+
if (server_ != nullptr) {
1501+
for (const auto& [peer, transfers] : transfers_by_peer) {
1502+
std::vector<int> src_ids;
1503+
std::vector<int> dst_ids;
1504+
src_ids.reserve(transfers.size());
1505+
dst_ids.reserve(transfers.size());
1506+
for (const auto& [src_id, dst_id] : transfers) {
1507+
src_ids.push_back(src_id);
1508+
dst_ids.push_back(dst_id);
1509+
}
1510+
server_->AsyncPush(
1511+
{peer}, src_ids, dst_ids, state->parallelism,
1512+
transport::MajorOrder::kLayerMajor, uuid,
1513+
static_cast<int>(pool_idx),
1514+
[this, uuid, generation = state->plan_generation](
1515+
absl::StatusOr<std::vector<int>> result) {
1516+
FinishPoolReshardSend(
1517+
uuid, generation,
1518+
result.ok() ? absl::OkStatus() : result.status());
1519+
});
1520+
}
1521+
return;
14941522
}
1495-
transport_server->AsyncPush(
1496-
{peer}, src_ids, dst_ids, state->parallelism,
1497-
transport::MajorOrder::kLayerMajor, uuid, static_cast<int>(pool_idx),
1498-
[this, uuid](absl::StatusOr<std::vector<int>> result) {
1499-
FinishPoolReshardSend(
1500-
uuid, result.ok() ? absl::OkStatus() : result.status());
1501-
});
15021523
}
1524+
FinishPoolReshardSend(
1525+
uuid, state->plan_generation,
1526+
absl::FailedPreconditionError("transport server is not running"));
15031527
}
15041528

15051529
void KVCacheManagerWithTransfer::FinishPoolReshardSend(
1506-
uint64_t uuid, const absl::Status& status) {
1530+
uint64_t uuid, uint64_t generation, const absl::Status& status) {
15071531
bool finished = false;
15081532
{
15091533
absl::MutexLock lock(mu_);
15101534
auto it = active_pool_reshard_sends_.find(uuid);
15111535
if (it == active_pool_reshard_sends_.end()) return;
15121536
auto& state = *it->second;
1537+
// Completion for an earlier registration of this uuid must not touch
1538+
// the current one's progress.
1539+
if (state.plan_generation != generation) return;
15131540
if (state.finalizing) return;
15141541
if (!status.ok()) {
15151542
LOG(ERROR) << "Pool reshard send failed uuid=" << uuid
@@ -1523,16 +1550,17 @@ void KVCacheManagerWithTransfer::FinishPoolReshardSend(
15231550
}
15241551
}
15251552
if (finished) {
1526-
absl::Status unregister = UnregisterActivePlan(uuid);
1527-
if (!unregister.ok() && !absl::IsNotFound(unregister)) {
1528-
LOG(ERROR) << "Failed to unregister pool reshard sender plan " << uuid
1529-
<< ": " << unregister;
1553+
UnregisterSettledPlan(uuid, generation);
1554+
if (!status.ok()) {
1555+
// A failed pool send retires its uuid so a late chunk lookup cannot
1556+
// resolve into the mirror once the plan is gone.
1557+
RetireTransferUuid(uuid);
15301558
}
15311559
absl::MutexLock lock(mu_);
15321560
auto it = active_pool_reshard_sends_.find(uuid);
15331561
if (it == active_pool_reshard_sends_.end()) return;
1534-
if (it->second->failed ||
1535-
(!unregister.ok() && !absl::IsNotFound(unregister))) {
1562+
if (it->second->plan_generation != generation) return;
1563+
if (it->second->failed) {
15361564
failed_recving_.insert(it->second->req_id);
15371565
} else {
15381566
done_sending_.insert(it->second->req_id);
@@ -1557,16 +1585,6 @@ absl::Status KVCacheManagerWithTransfer::PoolReshardRegisterRecv(
15571585
return absl::InvalidArgumentError(
15581586
"pool reshard receiver requires dst_mem_type=HBM");
15591587
}
1560-
{
1561-
absl::MutexLock lock(mu_);
1562-
if (active_recv_entries_.contains(plan.uuid())) {
1563-
return absl::AlreadyExistsError(
1564-
absl::StrCat("pool reshard recv UUID already active: ", plan.uuid()));
1565-
}
1566-
}
1567-
1568-
TF_RETURN_IF_ERROR(kv_cache::KVCacheManagerBase::RegisterActivePlan(
1569-
plan.uuid(), plan, /*is_sender=*/false));
15701588
RecvEntry recv_entry;
15711589
recv_entry.req_id = plan.req_id();
15721590
recv_entry.is_pool_reshard = true;
@@ -1591,8 +1609,32 @@ absl::Status KVCacheManagerWithTransfer::PoolReshardRegisterRecv(
15911609
}
15921610
}
15931611
{
1594-
absl::MutexLock lock(mu_);
1595-
active_recv_entries_[plan.uuid()] = std::move(recv_entry);
1612+
// One lifecycle step, like block plans: the receive state is armed
1613+
// first and the plan published last under the lifecycle lock, so an
1614+
// early inbound push can never observe the plan without its receiver,
1615+
// and a generation scopes every later cleanup to this registration.
1616+
absl::MutexLock lifecycle(plan_lifecycle_mu_);
1617+
if (kv_cache::KVCacheManagerBase::HasActivePlan(plan.uuid())) {
1618+
return absl::AlreadyExistsError(absl::StrCat(
1619+
"Plan with UUID ", plan.uuid(), " is already registered!"));
1620+
}
1621+
const uint64_t generation = ++plan_generation_counter_;
1622+
recv_entry.plan_generation = generation;
1623+
{
1624+
absl::MutexLock lock(mu_);
1625+
if (active_recv_entries_.contains(plan.uuid())) {
1626+
return absl::AlreadyExistsError(absl::StrCat(
1627+
"pool reshard recv UUID already active: ", plan.uuid()));
1628+
}
1629+
active_recv_entries_[plan.uuid()] = std::move(recv_entry);
1630+
}
1631+
absl::Status registered = kv_cache::KVCacheManagerBase::RegisterActivePlan(
1632+
plan.uuid(), plan, /*is_sender=*/false, {}, generation);
1633+
if (!registered.ok()) {
1634+
absl::MutexLock lock(mu_);
1635+
active_recv_entries_.erase(plan.uuid());
1636+
return registered;
1637+
}
15961638
}
15971639
return absl::OkStatus();
15981640
}
@@ -1922,7 +1964,7 @@ KVCacheManagerWithTransfer::CompleteReadRaw() {
19221964
const auto& entry = it->second;
19231965
if (entry->deadline <= now) {
19241966
failed_recving_.insert(entry->req_id);
1925-
settled_plans.emplace_back(it->first, 0);
1967+
settled_plans.emplace_back(it->first, entry->plan_generation);
19261968
auto erase_it = it++;
19271969
active_pool_reshard_sends_.erase(erase_it);
19281970
} else {
@@ -3403,11 +3445,13 @@ absl::Status KVCacheManagerWithTransfer::OnPoolReceived(size_t pool_idx,
34033445

34043446
void KVCacheManagerWithTransfer::LaunchEligiblePoolH2ds(uint64_t uuid) {
34053447
std::vector<std::pair<size_t, std::vector<int64_t>>> to_launch;
3448+
uint64_t generation = 0;
34063449
{
34073450
absl::MutexLock lock(mu_);
34083451
auto it = active_recv_entries_.find(uuid);
34093452
if (it == active_recv_entries_.end()) return;
34103453
RecvEntry& entry = it->second;
3454+
generation = entry.plan_generation;
34113455
if (entry.reshard_finalizing) return;
34123456
for (size_t pool_idx : entry.started_pool_indices) {
34133457
if (entry.h2d_launched_pools.count(pool_idx)) continue;
@@ -3436,17 +3480,19 @@ void KVCacheManagerWithTransfer::LaunchEligiblePoolH2ds(uint64_t uuid) {
34363480
for (auto& [pool_idx, chip_block_ids] : to_launch) {
34373481
auto future_or = H2dPoolBlocks(pool_idx, chip_block_ids);
34383482
if (!future_or.ok()) {
3439-
FinishPoolReshardRecvPool(uuid, pool_idx, future_or.status());
3483+
FinishPoolReshardRecvPool(uuid, pool_idx, generation,
3484+
future_or.status());
34403485
continue;
34413486
}
34423487
raiden::PjRtCopyFuture future = std::move(future_or).value();
34433488
{
34443489
absl::MutexLock lock(mu_);
34453490
++total_outstanding_ops_;
34463491
}
3447-
future.OnReady([this, uuid, pool_idx = pool_idx](auto status_or) {
3492+
future.OnReady([this, uuid, pool_idx = pool_idx,
3493+
generation](auto status_or) {
34483494
FinishPoolReshardRecvPool(
3449-
uuid, pool_idx,
3495+
uuid, pool_idx, generation,
34503496
status_or.ok() ? absl::OkStatus() : status_or.status());
34513497
// The callback reads manager state; destruction waits for it.
34523498
absl::MutexLock lock(mu_);
@@ -3463,13 +3509,17 @@ void KVCacheManagerWithTransfer::LaunchEligiblePoolH2ds(uint64_t uuid) {
34633509
}
34643510

34653511
void KVCacheManagerWithTransfer::FinishPoolReshardRecvPool(
3466-
uint64_t uuid, size_t pool_idx, const absl::Status& status) {
3512+
uint64_t uuid, size_t pool_idx, uint64_t generation,
3513+
const absl::Status& status) {
34673514
bool finished = false;
34683515
{
34693516
absl::MutexLock lock(mu_);
34703517
auto it = active_recv_entries_.find(uuid);
34713518
if (it == active_recv_entries_.end()) return;
34723519
RecvEntry& entry = it->second;
3520+
// Completion for an earlier registration of this uuid must not touch
3521+
// the current one's progress.
3522+
if (entry.plan_generation != generation) return;
34733523
if (entry.reshard_finalizing) return;
34743524
if (!status.ok()) {
34753525
entry.reshard_finalizing = true;
@@ -3489,24 +3539,26 @@ void KVCacheManagerWithTransfer::FinishPoolReshardRecvPool(
34893539
std::chrono::steady_clock::time_point start_time;
34903540
bool should_record_duration = false;
34913541
if (finished) {
3492-
absl::Status unregister = UnregisterActivePlan(uuid);
3493-
if (!unregister.ok() && !absl::IsNotFound(unregister)) {
3494-
LOG(ERROR) << "Failed to unregister pool reshard receiver plan " << uuid
3495-
<< ": " << unregister;
3496-
}
3497-
absl::MutexLock lock(mu_);
3498-
auto it = active_recv_entries_.find(uuid);
3499-
if (it == active_recv_entries_.end()) return;
3500-
if (!status.ok() || (!unregister.ok() && !absl::IsNotFound(unregister))) {
3501-
failed_recving_.insert(it->second.req_id);
3502-
active_recv_entries_.erase(it);
3503-
} else {
3504-
start_time = it->second.start_time;
3505-
should_record_duration = true;
3542+
UnregisterSettledPlan(uuid, generation);
3543+
SettleActions actions;
3544+
{
3545+
absl::MutexLock lock(mu_);
3546+
auto it = active_recv_entries_.find(uuid);
3547+
if (it == active_recv_entries_.end()) return;
3548+
if (it->second.plan_generation != generation) return;
3549+
if (!status.ok()) {
3550+
// A failed pool receive settles like any other receive: an open
3551+
// payload lease keeps the entry parked until its stream lets go.
3552+
SettleRecvLocked(uuid, /*failed=*/true, &actions);
3553+
} else {
3554+
start_time = it->second.start_time;
3555+
should_record_duration = true;
35063556

3507-
it->second.network_completed = true;
3508-
done_recving_.insert(it->second.req_id);
3557+
it->second.network_completed = true;
3558+
done_recving_.insert(it->second.req_id);
3559+
}
35093560
}
3561+
ApplySettleActions(&actions);
35103562
}
35113563
if (should_record_duration) {
35123564
RecordTransferDuration(

tpu_sync/core/kv_cache_manager_with_transfer.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,9 @@ class KVCacheManagerWithTransfer : public kv_cache::KVCacheManagerBase {
499499
struct PoolReshardSendEntry {
500500
std::string req_id;
501501
uint64_t uuid = 0;
502+
// Generation of the plan this send belongs to; settlement cleanup only
503+
// touches that registration.
504+
uint64_t plan_generation = 0;
502505
int parallelism = 8;
503506
int remaining_pool_peer_pushes = 0;
504507
bool failed = false;
@@ -528,9 +531,12 @@ class KVCacheManagerWithTransfer : public kv_cache::KVCacheManagerBase {
528531
// corruption; the arming worker validates for itself.
529532
absl::Status ValidatePoolReshardReceiverCoverage(
530533
const ::tpu_sync::rpc::StartTransferRequest& plan);
531-
void StartPoolReshardPush(uint64_t uuid, size_t pool_idx);
532-
void FinishPoolReshardSend(uint64_t uuid, const absl::Status& status);
534+
void StartPoolReshardPush(uint64_t uuid, size_t pool_idx,
535+
uint64_t generation);
536+
void FinishPoolReshardSend(uint64_t uuid, uint64_t generation,
537+
const absl::Status& status);
533538
void FinishPoolReshardRecvPool(uint64_t uuid, size_t pool_idx,
539+
uint64_t generation,
534540
const absl::Status& status);
535541
// Launches H2D uploads for every wire-complete pool whose order-rank
536542
// prerequisites (all lower-rank pools uploaded) are satisfied.

0 commit comments

Comments
 (0)