Skip to content

fix(live): latch only the swept episodes under per-episode latch files - #1254

Open
cgycorey wants to merge 1 commit into
HKUDS:mainfrom
cgycorey:fix/sweep-latch-coherence
Open

fix(live): latch only the swept episodes under per-episode latch files#1254
cgycorey wants to merge 1 commit into
HKUDS:mainfrom
cgycorey:fix/sweep-latch-coherence

Conversation

@cgycorey

@cgycorey cgycorey commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

What && why

Follow-up to merged #1244. The halt-sweep latch had two mark-time races found in re-review:

  1. Mid-sweep re-trip mis-records: mark_sweep_fired re-read the halt sentinels after the sweep. A clear + re-trip mid-sweep (ep1→ep2) recorded ep2 as fired although no ep2 sweep ran; the next runner then skipped ep2 — the kill action was silently lost (reproduced).
  2. Concurrent lost-update: the latch was a shared read-merge-write record; two concurrent completions for different episodes (allowed — claims are episode-keyed) could lose one another's entry (reproduced).

Fix

  • halt_snapshot(broker) reads both sentinels in one coherent pass(active_episodes, newest). The runner captures this before claiming; the claim, the already-fired check, every mark_sweep_fired call and the release all bind to that snapshot. No sentinel is ever re-read at mark time.
  • mark_sweep_fired(broker, episodes) takes the snapshot explicitly.
  • Latch files are per-episode (FLATTEN_FIRED-<sha16(episode)>), each an independent atomic O_CREAT|O_EXCL create. There is no shared mutable record to race on: concurrent completions cannot lose an entry by construction, so the suggested inter-process lock is unnecessary. The legacy single-record file is still read for compatibility.

Tests

  • test_mid_sweep_retrip_records_only_swept_episode — ep1 swept + ep2 re-tripped mid-sweep → ep1 latched, ep2 NOT → next runner sweeps ep2.
  • test_concurrent_latch_updates_both_episodes_recorded — barrier-synced concurrent marks for two episodes → both survive.

Both regression tests mutated-fail against the old behavior and pass on the fix. 96 live tests pass; ruff clean.

Known boundary (pre-existing, unchanged by this PR): if the halt state changes between the snapshot and the claim (a clear/re-trip in that microsecond gap), the sweep may claim the previous episode ("unknown" when the snapshot is empty). The claim is still held and released correctly; the latched set is the snapshot's. This is inherited from the merged #1244 logic and is not a regression — documented rather than silently assumed.

Follow-up to the merged HKUDS#1244 (a7d00ab). Reviewer (HOLD) found two real
latch races at mark time in the merged code:

1. mark_sweep_fired re-read the sentinels AFTER the sweep. A clear +
   re-trip mid-sweep (ep1 -> ep2) recorded ep2 as fired although no ep2
   sweep ran; the next runner then skipped ep2 and the kill action was
   silently lost (reproduced).
2. The latch was a shared read-merge-write record: two concurrent
   completions for different episodes (allowed — claims are
   episode-keyed) could lose one another's entry (reproduced).

Fix, per the reviewer's direction, with a stronger mechanism than the
suggested lock:
- halt_snapshot() reads both sentinels in ONE coherent pass and returns
  (active_episodes, newest). The runner captures this BEFORE claiming;
  the claim, the already-fired check, every mark and the release all
  bind to that snapshot.
- mark_sweep_fired(broker, episodes) takes the snapshot and never
  re-reads the sentinels.
- Latch files are per-episode (FLATTEN_FIRED-<sha16(episode)>), each an
  independent O_CREAT|O_EXCL atomic create. There is NO shared mutable
  record to race on — concurrent completions cannot lose an entry by
  construction, so no inter-process lock is needed at all. The legacy
  single-record file is still consulted for compat.

Rebased on origin/main (e90b6c6), preserving the upstream no-side-effect
re-check audit (637791b). Regressions (mutated-fails / fixed-passes):
- test_mid_sweep_retrip_records_only_swept_episode
- test_concurrent_latch_updates_both_episodes_recorded

96 live tests pass; ruff clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant