fix(914): thread-local ALL_OPS/VECTOR_CLOCK in cfg(test) so compaction tests don't race
Root cause was not the persist channel (the test-mode channel is unbounded and its receiver is leaked, so sends never fail). It was that `ALL_OPS` and `VECTOR_CLOCK` were process-wide `OnceLock` globals while `CRDT_STATE` was already thread-local — so one test thread's `apply_compaction` would prune another test thread's freshly-written ops out of the shared journal, and the subsequent `all_ops_json()` read in `compaction_reduces_ops` would return fewer than the 5 it had just written. Mirror the pattern already used for `CRDT_STATE` and `SnapshotState`: in `cfg(test)` use thread-local `OnceLock<Mutex<...>>`s for the op journal and vector clock, accessed via new `all_ops_lock()` / `vector_clock_lock()` helpers. Production code path is unchanged (still the global statics set during `init()`). Touches ops/read/snapshot call sites to go through the helpers. Note in passing that this overlaps backlog story 518; that story is about the production-side persist path, this is the cfg(test)-only journal-isolation slice. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,8 +35,8 @@ pub(super) use indices::{
|
||||
rebuild_index, rebuild_merge_job_index, rebuild_node_index, rebuild_test_job_index,
|
||||
rebuild_token_index,
|
||||
};
|
||||
pub(crate) use statics::{ALL_OPS, VECTOR_CLOCK};
|
||||
pub(super) use statics::{SYNC_TX, track_op};
|
||||
pub(crate) use statics::{all_ops_lock, vector_clock_lock};
|
||||
|
||||
// ── CrdtState struct ─────────────────────────────────────────────────
|
||||
|
||||
@@ -152,6 +152,13 @@ pub fn init_for_test() {
|
||||
});
|
||||
let _ = statics::CRDT_EVENT_TX.get_or_init(|| broadcast::channel::<CrdtEvent>(256).0);
|
||||
let _ = statics::SYNC_TX.get_or_init(|| broadcast::channel::<SignedOp>(1024).0);
|
||||
let _ = statics::ALL_OPS.get_or_init(|| Mutex::new(Vec::new()));
|
||||
let _ = statics::VECTOR_CLOCK.get_or_init(|| Mutex::new(VectorClock::new()));
|
||||
// Per-thread op journal + vector clock — keeps parallel tests' writes
|
||||
// from corrupting each other's view of ALL_OPS (notably, one thread's
|
||||
// `apply_compaction` could otherwise prune another thread's ops).
|
||||
statics::ALL_OPS_TL.with(|lock| {
|
||||
let _ = lock.set(Mutex::new(Vec::new()));
|
||||
});
|
||||
statics::VECTOR_CLOCK_TL.with(|lock| {
|
||||
let _ = lock.set(Mutex::new(VectorClock::new()));
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user