fix(WRK-32): pre-baseline pump for write-completion correlation + honest contract docs

Write-completion correlation is recency-based per (server_handle,
item_handle): OnWriteComplete carries no per-call token, so the executor
attributes the first completion whose version exceeds a pre-call baseline
to that write. A stale completion from an earlier timed-out write to the
same handle could ride the next write's reply if it was already queued
when the baseline was captured.

Add CaptureCompletionBaseline (Write, Write2, WriteSecured,
WriteSecured2) to run one pumpStep() immediately before snapshotting
CurrentVersion, so an already-queued stale completion folds into the
baseline instead of being available to the next wait. This narrows, but
per the finding cannot close, the correlation gap -- a completion queued
after the pump step and before the COM call returns (or during the wait)
is still indistinguishable from the new write's own completion; closing
it needs an MXAccess-side correlation token that does not exist.

Rewrite the AwaitWriteCompletion doc comment to state the recency-based
contract honestly instead of implying exact per-call matching, and add
the same contract to docs/MxAccessWorkerInstanceDesign.md's
write-completion section.

Deferred: the reply-field comment in
src/ZB.MOM.WW.MxGateway.Contracts/Protos/mxaccess_gateway.proto is not
edited here. A comment-only proto change still requires regenerating
every generated artifact (C#, Go, Python, Java, Rust, protoset) in the
same commit per repo convention, and this macOS box cannot verify all
five client generator pins together for a Low-severity doc-only change.
The contract is documented in MxAccessWorkerInstanceDesign.md instead;
the existing proto comment ("concurrent writes... can swap rows") is not
wrong, just less precise than the new doc section.
This commit is contained in:
Joseph Doherty
2026-08-20 09:20:28 -04:00
parent 93edb1b74d
commit 20a3eefb1e
2 changed files with 72 additions and 5 deletions
+24
View File
@@ -317,6 +317,30 @@ whose write workload is effectively fire-and-forget — no consumer reads the
reply's `statuses` — can lower it, or set `0` to skip the wait entirely and
reply on acceptance alone.
**Write-completion correlation is recency-based, not per-call.** MXAccess's
`OnWriteComplete` callback carries no correlation token — only the server/item
handle pair — so `MxAccessCommandExecutor` correlates a completion to a write
by *version*: it snapshots `MxAccessWriteCompletionCache.CurrentVersion` for
the handle as a baseline before the COM call, then reports the first
completion whose version exceeds that baseline as the write's outcome
(`AwaitWriteCompletion`). This is exact for the common case, but if an earlier
write to the same handle times out unconfirmed — its `OnWriteComplete` still
undelivered when the wait gives up — and the caller immediately issues another
write to the same tag, the stale callback can dispatch during the second
write's pump-wait and be reported as *its* outcome. The executor mitigates the
most common instance of this (`CaptureCompletionBaseline`) by running one
`pumpStep()` immediately before capturing the baseline, so a completion already
queued for the handle folds into the baseline instead of being available to
ride the next write's reply; this closes the gap for a callback that arrived
before the new write started, but a callback that MXAccess queues after that
pump step and before the COM call returns (or during the wait itself) is still
indistinguishable from the new write's own completion. Full closure is not
possible without an MXAccess-side correlation token, which does not exist —
this is a property of the COM surface, not a gap this worker introduced (a
native client correlating by handle has the identical ambiguity). The exposure
is bounded: `statuses` is an enrichment field, and `Hresult`/`ProtocolStatus`
stay acceptance-only regardless of which completion (if any) was attributed.
## COM Creation
The MXAccess analysis source at `C:\Users\dohertj2\Desktop\mxaccess` identifies