Skip to content

Commit 8f0c9a8

Browse files
committed
fix(session): re-verify local vShard ownership before the durable WAL append
run_commit resolves a vShard as Local, but a leadership handoff can land during the ResolveTxn await that follows. Without a re-check right before the durable append, the transaction redo could be written to a WAL this node no longer owns, and the later batch dispatch (which re-resolves leadership) would then reject the now-non-local commit, leaving an orphaned durable redo record while the client is told the commit aborted. Abort before any durable write instead, so the failure stays side-effect-free and the client's retry can route through Calvin's replicated barrier.
1 parent 3b95bf2 commit 8f0c9a8

1 file changed

Lines changed: 20 additions & 0 deletions

File tree

  • nodedb/src/control/server/shared/session

nodedb/src/control/server/shared/session/commit.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,26 @@ async fn dispatch_single_shard(
335335
}
336336
};
337337

338+
// Re-verify local vShard ownership immediately before the durable WAL
339+
// append. `run_commit` resolved this vShard as `Local`, but a leadership
340+
// handoff can land during the `ResolveTxn` await above. Without this
341+
// re-check the transaction redo would be appended to a WAL this node no
342+
// longer owns, and the batch dispatch below (which re-resolves leadership)
343+
// would then reject the now-non-local commit — leaving an orphaned durable
344+
// redo record behind while the client is told the commit aborted. Aborting
345+
// here, BEFORE any durable write, keeps the failure side-effect-free and
346+
// retryable: the client's retry re-enters `run_commit`, sees the vShard is
347+
// non-local, and routes the commit through Calvin's replicated barrier.
348+
if !matches!(
349+
crate::control::server::graph_dispatch::cluster_resolve::resolve_for_vshard(
350+
state,
351+
vshard_id.as_u32(),
352+
),
353+
RouteDecision::Local
354+
) {
355+
return Some(AbortReason::Serialization);
356+
}
357+
338358
// 2. Write-ahead the transaction as ONE replayable `TransactionRedo` record
339359
// (each sub-op keeps its real engine `record_type`). `None` when the txn
340360
// has no durable writes (all reads / CRDT / text). Its LSN stamps the

0 commit comments

Comments
 (0)