docs(buglog): bug-1923 fixed (16616d96), synced to Gitea #348

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-10-08 08:11:37 +00:00
parent 16616d9688
commit e06bdc290b
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
3 changed files with 31 additions and 17 deletions

View file

@ -1,8 +1,8 @@
---
id: "bug-1923"
status: "open"
status: "fixed"
status_updated: "2026-10-08"
status_note: "newly logged"
status_note: ""
component: "scheduler"
severity: "wrong-result"
timestamp: "2026-10-08T07:17:34Z"
@ -11,9 +11,10 @@ occurrences: 1
files: ["src/scheduler/src/snapshot.c", "src/scheduler/src/snapshot_load.c", "src/scheduler/src/snapshot_wire.c"]
tags: ["sim", "snapshot", "resvals", "replay", "persistence"]
related: []
fix_commits: ["16616d96"]
gitea_issue: 348
gitea_synced_status: "open"
gitea_synced_hash: "d364cfaa24aa"
gitea_synced_status: "fixed"
gitea_synced_hash: "735fa9567f4e"
---
# bug-1923: xce_scheduler --sim drops shared resource values from the snapshot (license shows value=(none))
@ -60,15 +61,26 @@ Durable copies are in `/home/claude/tmp/bug-1923/`: `fixA/contention.replay` has
- Evidence: `/home/claude/tmp/bug-1923/` (replays, generator, decoder, probe script)
## Root cause
TODO: required for fixed/partial
Two separate defects on the SnapshotEntry save/load path, both pre-existing (origin unknown):
- **Read side, `src/scheduler/src/snapshot_load.c:129`:** the shared-pool loop skipped any resval with `rv->host != NULL`. The writer stores shared values with `host = NULL`, but after the capnp round trip (`write_snapshot_entry()` in recording.c → `.replay` → `read_SnapshotEntry`/`decode_SnapshotEntry` in sim.c) a NULL host decodes as `""`. So every shared value in a snapshot read from a file was silently skipped, and `core->sharedInst` started empty. The `RESVALS_FULL` event path (`core_events.c:1124-1147`) has no host check, which is why the workaround loaded. A non-empty host only appears on `RESVALS_DELTA` (routing a value to one worker).
- **Write side, `src/scheduler/src/snapshot.c:431`:** the shared-pool loop was bounded by `core->sharedInst.count`. `zarray_put_ex` (`src/lib/zarray.c`) counts filled slots, not highest index + 1, so in a sparse `sharedInst` the values at higher indices were never written. In the baseline fixture, `license` (idx 2) and `limit=cluster_slots` were dropped.
- **Same defect, per worker, `src/scheduler/src/snapshot_wire.c:242`:** the loop over `w->resvals` was bounded by `w->resvals.count`. A worker never holds a value at a global resource's idx, so its resvals always have holes, and `limit=host_slots` was dropped for every worker.
- Scope checked: no other `.count`-bounded loop over a sparse resval zarray remains in src/scheduler or src/lib. `repl_info.c:524` and `resource.c:262` already use `.caps`; the other `.count` uses are dense lists or log lines.
## Fix
TODO: required for fixed/partial
- `snapshot_load.c`: a resval with a NULL **or empty** host is a shared-pool entry; only a named host is skipped.
- `snapshot.c`: the shared-pool loop is bounded by `sharedInst.caps` (the project convention for sparse resval arrays). Empty slots were already skipped via `zarray_get` returning NULL.
- `snapshot_wire.c`: the per-worker loop is bounded by `w->resvals.caps`.
- These changes make the shared and per-worker values round-trip through both the write side and the load seam (persistence.md, snapshot field-completeness).
- Compatibility: replay files written by the old writer lack the dropped values, so the reader fix alone can't recover them (`/home/claude/tmp/bug-1923/fixA` still shows `value=(none)`). Files written from now on carry them.
- Fixture: `src/scheduler/testsuite/fixtures/repl_trace_baseline/set_output.replay` was regenerated. Only its SnapshotEntry changed (+304 bytes: `license`, `limit=cluster_slots` and two per-worker `limit=host_slots` values). The header differs only in the ignored wall-clock field, the transcript, `sim_output.replay` and verify output are unchanged, and messages 2-15 are byte-identical.
## Verification
- Test: TODO: name + file; RED before the fix, GREEN after
- Suites: TODO: suite results and build profile
- Live: TODO: soak/deploy run, or n/a and why
- Test: new `src/scheduler/testsuite/test_snapshot_sparse_resvals.c` ("scheduler snapshot sparse resvals test"), which links the real scheduler sources. RED before the fix: 6 Tests 5 Failures (`test_save_keeps_sparse_shared_resval`, `test_save_keeps_sparse_worker_resval`, `test_save_load_round_trip_keeps_sparse_resvals`, `test_load_takes_shared_resval_with_empty_host`, and `test_replay_round_trip_keeps_sparse_resvals`, which goes through the real `.replay` writer and the `--sim` start-up reader). The guard test `test_load_skips_resval_with_named_host` passed. GREEN after: 6 Tests 0 Failures.
- Suites: scheduler meson suite Ok 100 / Fail 0, asan (`src/xce.build` in the fix worktree); `scheduler repl trace baseline test` OK with the regenerated `set_output.replay`.
- Live: `xce_scheduler --sim /home/claude/tmp/bug-1923/fixC/contention.replay --interactive` on the fixed asan build: `info resource license` shows `value=2.00 used=0.00`, then `value=2.00 used=2.00` after `step`, and jobs 102 and 103 DISPATCH (before the fix: `value=(none)`, and both PEND). A variant-A replay regenerated with the fixed writer also loads `value=2.00`. No HA soak was needed: the change is limited to the scheduler's snapshot save/load and does not touch the controller or WAL.
## References
- Related: none
- Related: bug-1568 (same `snapshot_wire.c` worker-resval loop; that bug added the missing-resource diagnostic)
- Docs: .kiro/steering/persistence.md (Snapshot Save/Load Field-Completeness)
- Found by: .kiro/specs/scheduler-sim-decision-trace task 1.1