11# External References Digest (working note for discovery agents)
22
3- This is scaffolding for the antithesis-research run on ** disk buffers v2**
4- (` lib/vector-buffers/src/variants/disk_v2/ ` ). User scope answer: * "Whatever you
5- have access to. You have your MCPs."* — so in-repo docs/RFCs plus Datadog
6- internal doc/Jira were consulted. Key findings condensed below so per-focus agents
7- don't need to re-fetch.
3+ Scaffolding for the antithesis-research run on ** disk buffers v2**
4+ (` lib/vector-buffers/src/variants/disk_v2/ ` ). User scope: * "Whatever you have
5+ access to. You have your MCPs."* — in-repo docs/RFCs plus Datadog internal doc/Jira.
6+ Findings condensed below so per-focus agents need not re-fetch.
87
98## In-repo references
109
@@ -13,102 +12,68 @@ don't need to re-fetch.
1312- ` lib/vector-buffers/src/variants/disk_v2/mod.rs ` — authoritative design doc
1413 (module-level comment): on-disk format, ledger, record IDs, recovery.
1514
16- ## Claimed guarantees (from ` mod.rs ` design doc + buffer spec + internal doc)
17-
18- - Data files never exceed 128MB; ≤ 65,536 files; buffer ≤ ~ 8TB.
19- - All records checksummed with ** CRC32C** ; records written
20- sequentially/contiguously; a record never spans two data files.
21- - Writers create+write data files; readers read+delete them. Reader deletes a
22- data file ** only after all records in it are acknowledged** (whole-file
23- deletion, never partial truncation).
24- - Ledger (` buffer.db ` , memory-mapped) tracks ` writer_next_record_id ` ,
25- ` writer_current_data_file_id ` , ` reader_current_data_file_id ` ,
26- ` reader_last_record_id ` . Fields updated atomically, but ** not** atomically
27- w.r.t. reader/writer activity.
28- - Record IDs are monotonic and encode event count: record ID N with next record
29- M means the record holds M−N events. Used to compute buffer event count and to
30- detect gaps / dropped events after corruption.
31- - ** Durability:** data is fsync'd every ** 500ms** (` DEFAULT_FLUSH_INTERVAL ` ).
32- Page-cache flush happens on every ` flush() ` (readers see data immediately on
33- Linux); full fsync only every 500ms. ** Data-loss window on crash = up to 500ms
34- of unsynced writes** (when e2e acks off). Graceful shutdown flushes everything
35- → no loss.
36- - Min buffer ` max_size ` ~ 256MB; ` DEFAULT_MAX_DATA_FILE_SIZE ` 128MB;
37- ` DEFAULT_MAX_RECORD_SIZE ` = 128MB; ` DEFAULT_WRITE_BUFFER_SIZE ` 256KB.
38- - Endianness: files are host-endian; not portable across architectures.
39- - Delivery semantics with e2e acks + disk buffer = ** at-least-once** : crash after
40- buffer write but before downstream ack → replay on restart → ** possible
41- duplicates** (downstream must dedup).
15+ Claimed guarantees (CRC32C, whole-file deletion, monotonic event-count IDs, 500ms
16+ fsync window, at-least-once with duplicates on replay, size constants) live in
17+ ` sut-analysis.md ` and the per-property files — not duplicated here.
4218
4319## Known bugs / incidents (HIGH-VALUE Antithesis targets)
4420
45211 . ** Ledger ` total_buffer_size ` AtomicU64 underflow → permanent writer deadlock**
46- (Vector #21683 , partially mitigated by PR #23561 on the * reporter* side only;
47- the ledger atomic still wraps).
48- - ` decrement_total_buffer_size ` (ledger.rs, ` fetch_sub ` at ledger.rs:319) does
49- raw ` fetch_sub(amount, AcqRel) ` with ** no saturation** . If `amount >
50- current_value ` , the atomic wraps to ≈ 2^64. Under the ` antithesis` feature
51- this site now carries a committed ` assert_always_greater_than_or_equal_to!(total_buffer_size, amount) `
52- detector at ledger.rs:313 — it reports the wrap, it does not prevent the subtraction.
22+ (Vector #21683 , mitigated by PR #23561 on the * reporter* side only; the ledger
23+ atomic still wraps).
24+ - ` decrement_total_buffer_size ` does raw ` fetch_sub(amount, AcqRel) ` , ** no
25+ saturation** . If ` amount > current_value ` the atomic wraps to ≈ 2^64. Under the
26+ ` antithesis ` feature this site carries a committed
27+ ` assert_always_greater_than_or_equal_to!(total_buffer_size, amount) ` detector —
28+ reports the wrap, does not prevent the subtraction.
5329 - Then ` total_buffer_size + unflushed_bytes ` is always astronomical →
54- ` is_buffer_full() ` returns true forever → ` can_write_record() ` false forever
55- → writer's ` ensure_ready_for_write() ` (writer.rs ~ 1001-1020) loops on
56- ` ledger.wait_for_reader().await ` and never recovers. ** Writer deadlocks
57- permanently.**
58- - Trigger: crash/reboot/abrupt-shutdown that leaves a data file whose on-disk
59- size and readable-record bytes disagree, combined with the reader running
60- through that file on restart. Partial writes at file-rotation boundaries are
61- the most plausible cause. Not deterministic per-restart, but not exotic.
62- - Reporter-side gauges use ` saturating_sub ` (PR #23561 ) so the * dashboard*
63- no longer shows 2^64, but the ledger control-path atomic is unfixed.
30+ ` is_buffer_full() ` true forever → ` can_write_record() ` false forever → writer's
31+ ` ensure_ready_for_write() ` loops on ` ledger.wait_for_reader().await ` forever.
32+ ** Writer deadlocks permanently.**
33+ - Trigger: crash/reboot/abrupt-shutdown leaving a data file whose on-disk size and
34+ readable-record bytes disagree, plus the reader running through it on restart.
35+ Partial writes at file-rotation boundaries are likeliest. Not deterministic
36+ per-restart, but not exotic.
37+ - Reporter gauges use ` saturating_sub ` (PR #23561 ) so the * dashboard* no longer
38+ shows 2^64, but the ledger control-path atomic is unfixed.
6439
65- 2 . ** Disk buffer stall + silent event drops during config reload**
66- (Vector #24948 , PR #24949 ; directly implicated in the ** internal config-reload incident non-prod
67- incident** ).
40+ 2 . ** Disk buffer stall + silent event drops during config reload** (Vector #24948 ,
41+ PR #24949 ; implicated in the ** internal non-prod config-reload incident** ).
6842 - Old writer dropped while events still in-flight → events lost without
6943 accounting.
7044 - ` track_dropped_events ` passes ` 0 ` for ` byte_size ` → permanent drift in
7145 buffer-size metrics.
72- - ` synchronize_buffer_usage() ` re-seeds metrics while the old reporter may
73- still run → double-counted metric spikes; then a metrics gap between old
74- reporter teardown and the first tick (2s) of the new reporter .
46+ - ` synchronize_buffer_usage() ` re-seeds metrics while the old reporter may still
47+ run → double-counted spikes, then a metrics gap between old-reporter teardown
48+ and the new reporter's first tick (2s).
7549
76503 . ** ` component_discarded_events_total ` blind to buffer drops** (Vector #24606 ,
7751 #24144 ). When a disk buffer fills and ` drop_newest ` fires, only
78- ` buffer_discarded_events_total ` increments; the component-level discarded
79- counter stays 0 → silent data loss on dashboards. ` BufferEventsDropped::emit() `
80- in ` lib/vector-buffers/src/internal_events.rs ` never calls
81- ` ComponentEventsDropped ` .
52+ ` buffer_discarded_events_total ` increments; the component-level discarded counter
53+ stays 0 → silent data loss on dashboards. ` BufferEventsDropped::emit() ` in
54+ ` lib/vector-buffers/src/internal_events.rs ` never calls ` ComponentEventsDropped ` .
8255
83- 4 . ** Buffer size gauges stuck non-zero / negative** (Vector #23995 , #17666 ,
84- # 21683 ). Reporter ` current() = total_entered.saturating_sub(total_left) ` ;
85- stuck-at-non-zero still open.
56+ 4 . ** Buffer size gauges stuck non-zero / negative** (Vector #23995 , #17666 , # 21683 ).
57+ Reporter ` current() = total_entered.saturating_sub(total_left) ` ; stuck-at-non-zero
58+ still open.
8659
87- 5 . ** Component tags lost for sinks using disk buffers** (OPA-5380): components
88- paused for IO at init time lose ` component_* ` labels on later-registered
89- metrics (utilization, etc.).
60+ 5 . ** Component tags lost for sinks using disk buffers** (OPA-5380): components paused
61+ for IO at init time lose ` component_* ` labels on later-registered metrics
62+ (utilization, etc.).
9063
9164## Existing test strategy (so we don't duplicate it)
9265
9366- In-repo: extensive ` proptest ` + ** model-based testing** under
94- ` variants/disk_v2/tests/model/ ` (a reference model + action sequencer +
95- in-memory filesystem). Unit tests for acknowledgements, initialization,
96- known_errors, size_limits, invariants, record.
97- - Datadog internal: an E2E ** chaos test** that SIGKILLs the worker 3× with e2e acks
98- enabled and asserts every event is delivered end-to-end. Antithesis should go
99- beyond: explore fault * timing/interleavings* (partial writes at rotation,
100- fsync-vs-crash windows, reader/writer races on the mmap'd ledger) that a fixed
101- 3×SIGKILL test cannot.
67+ ` variants/disk_v2/tests/model/ ` (reference model + action sequencer + in-memory
68+ filesystem). Unit tests for acknowledgements, initialization, known_errors,
69+ size_limits, invariants, record.
70+ - Datadog internal: an E2E ** chaos test** SIGKILLing the worker 3× with e2e acks,
71+ asserting every event delivered end-to-end. Antithesis goes beyond, exploring
72+ fault * timing/interleavings* (partial writes at rotation, fsync-vs-crash windows,
73+ reader/writer races on the mmap'd ledger) a fixed 3×SIGKILL test cannot.
10274- A ** major lock-contention performance issue** affected all disk-buffer users
10375 (writer throughput ~ 90 MiB/s capped by contention) — points at writer/reader
10476 coordination hot paths.
10577
106- ## Notes on faults
107-
108- - Crash-recovery properties require ** node termination faults** (often disabled
109- by default in Antithesis tenants) — flag this in the catalog.
110- - The disk buffer is ** single-process** (intra-Vector reader+writer sharing an
111- mmap'd ledger). Network/partition faults are largely irrelevant to the buffer
112- itself; the strong levers are node kill/restart, node hang, CPU throttling
113- (exposes the fsync/flush timing windows and lock contention), and filesystem
114- state across restart.
78+ (Fault-lever analysis — single-process buffer, node-kill required for
79+ crash-recovery — lives in ` sut-analysis.md ` and ` deployment-topology.md ` .)
0 commit comments