BranchCache.Put is unconditional last-Put-wins (execution/commitment/branch_cache.go, store), for read-fills and flush-refreshes alike. In-envelope writers (canonical read-fills and Commit refreshes under the exec-module semaphore) are coherent today, but nothing enforces it — a future out-of-envelope writer poisons the cache silently, the #22152 class. #22198 removed the known out-of-envelope writers (payload builds, RPC-side SDs); a PutIfAbsent-style read-fill discipline (mirroring #22146's StateCache fix) would add defense in depth so the next one is contained by construction.
Two constraints found while analyzing this during #22198's review — the obvious designs don't work:
- Naive keep-newest breaks the unwind path: the
maxStep fall-through in getLatestMetered relies on a read-fill deliberately replacing an entry with an older bounded value during an in-flight unwind.
- A same-epoch txN-monotonicity check (assert or gate) is invalid due to stamping asymmetry: DB-sourced read-fills stamp the step high-water (
lastTxNumOfStep, since the exact write txN isn't recoverable from the step-keyed record) while Commit refreshes stamp exact per-key write txNs — so legitimate flows lower txN within an epoch (cold key read-filled then committed), and a stale same-step fill does not lower it. Any discipline must account for the two txN granularities (e.g. distinguish read-fill vs refresh writes explicitly).
BranchCache.Putis unconditional last-Put-wins (execution/commitment/branch_cache.go,store), for read-fills and flush-refreshes alike. In-envelope writers (canonical read-fills andCommitrefreshes under the exec-module semaphore) are coherent today, but nothing enforces it — a future out-of-envelope writer poisons the cache silently, the #22152 class. #22198 removed the known out-of-envelope writers (payload builds, RPC-side SDs); a PutIfAbsent-style read-fill discipline (mirroring #22146's StateCache fix) would add defense in depth so the next one is contained by construction.Two constraints found while analyzing this during #22198's review — the obvious designs don't work:
maxStepfall-through ingetLatestMeteredrelies on a read-fill deliberately replacing an entry with an older bounded value during an in-flight unwind.lastTxNumOfStep, since the exact write txN isn't recoverable from the step-keyed record) whileCommitrefreshes stamp exact per-key write txNs — so legitimate flows lower txN within an epoch (cold key read-filled then committed), and a stale same-step fill does not lower it. Any discipline must account for the two txN granularities (e.g. distinguish read-fill vs refresh writes explicitly).