You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Persist batches in Creating, initialize assignments and reverse indexes idempotently, and resume the same batch across queue redelivery.
Jira Issues: CODEM-304
// TODO: if capacity is full, wait here for other requests to accumulate to batch them together, or include a request into an existing batch if it's not too late.
returnfmt.Errorf("failed to update batch dependent index for existing batchID=%s and new batchID=%s: %w", depID, batch.ID, err)
185
-
}
186
-
}
187
-
188
-
// Create new reverse index entry for the new batch. It would be empty for now, but will be updated as new batches are created that conflict with this batch.
// T3 cancel.markCancelling CAS 1→2 → R{State: Cancelling, Version: 2}
212
-
// T4 cancel.findActiveBatch(R) → none (batch has not been Created yet)
206
+
// T4 cancel searches active batches → none (batch has not been Created yet)
213
207
// T5 cancel.cancelRequest CAS 2→3 → R{State: Cancelled, Version: 3}
214
208
// T6 batch.IsRequestStateHalted(R) → false (stale in-memory copy from T1)
215
209
// T7 batch.BatchStore.Create(B{[R]}) → orphan batch containing a cancelled R
216
210
//
217
-
// After T7 the orphan batch flows through speculate → merge → conclude;
218
-
// conclude does NOT gate on the source request state when writing the terminal
219
-
// state, so it would CAS the request from Cancelled back to Landed, silently
220
-
// undoing the user's cancel.
211
+
// After T7 the orphan batch can still flow through speculate → merge even though its only request was cancelled.
212
+
// Conclude preserves a different terminal request outcome, but the batch still performs invalid work and can participate in the dependency graph without owning a live request.
221
213
//
222
214
// The CAS below collapses that window. Whichever of batch.UpdateState(...,
223
215
// RequestStateBatched) and cancel.markCancelling(... RequestStateCancelling)
224
216
// reaches storage first wins; the loser sees storage.ErrVersionMismatch:
225
217
// - If cancel won: this CAS fails. We ack the message (cancel will drive R
226
-
// to its terminal state on its own; no batch is needed). The reverse-index
227
-
// entry above becomes a dangling BatchDependent — tolerated per the
228
-
// "downstream should handle stale entries" contract on this store.
218
+
// to its terminal state on its own; no batch or reverse-index residue was
219
+
// written).
229
220
// - If batch won: cancel.markCancelling will fail with ErrVersionMismatch
230
221
// on its next attempt, re-fetch R, observe RequestStateBatched, and take
231
222
// the batch-cancellation branch (which terminates the whole batch).
232
223
//
233
-
// Note on re-delivery: a retry of a batch message that already CAS'd R to
234
-
// Batched but failed before/after BatchStore.Create lands in this code with
235
-
// R already in RequestStateBatched. The top-level IsRequestStateHalted check
236
-
// does NOT include Batched (Batched is forward-progress, not halted), so we
237
-
// reach here and re-CAS Batched → Batched (a version-only bump). The bump
238
-
// keeps the same serialization invariant on every attempt — if cancel sneaks
239
-
// in between our Get and this CAS, our version is stale and we abandon, just
240
-
// like the first-delivery case. The cost is an extra batch (the previous
241
-
// attempt may have already created one) which is tolerated per the comment
242
-
// on BatchStore.Create below.
224
+
// On redelivery, a persisted batch is resumed with the same ID.
225
+
// If the prior attempt failed after the request CAS but before BatchStore.Create, no batch exists to resume.
226
+
// The next attempt performs a version-only Batched → Batched CAS before creating a fresh batch so cancellation still races against a current request version.
243
227
//
244
-
// Residual window: a thin race remains between this CAS and BatchStore.Create.
245
-
// During that window cancel.findActiveBatch can still observe R in Batched
246
-
// with no batch yet persisted, and take the request-only cancel path — which
247
-
// then leaves R in Cancelled and the batch we are about to create orphaned.
248
-
// Fully closing this requires cancel-side wait/retry when its pre-CAS
249
-
// observation was RequestStateBatched; deferred to a follow-up since the
250
-
// window is narrow (one storage round-trip) and the user-visible outcome
251
-
// (request cancelled) is still correct — the orphan batch just gets
252
-
// reconciled by conclude as if it had no requests to act on.
228
+
// During the CAS → Create window, the cancel controller observes Batched but cannot yet resolve an assignment.
229
+
// It retries rather than taking the request-only cancellation path, closing the remaining orphan-batch race.
// This is the final operation that concludes the batch creation process. If it fails, BatchDependents will be pointing to a batch id that does not exist.
275
-
// We do not reuse batch ids, a retry of this operation will create a new batch with a new ID. The downstream logic that operates on BatchDependent should be able to handle stale entries.
250
+
// Persist the batch before creating any references to it.
251
+
// Creating batches are visible to ownership and cancellation lookups but excluded from dependency analysis and normal processing.
0 commit comments