@@ -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
14371454void 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
15051529void 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
34043446void 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
34653511void 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 (
0 commit comments