Skip to content

feat(mq): MQ effect records — kill-9-durable streams/PEL/DLQ/triggers + WAL recycle plane guard (Wave B stage 2a)#291

Merged
pilotspacex-byte merged 7 commits into
mainfrom
feat/wave-b-mq-effect-records
Jul 12, 2026
Merged

feat(mq): MQ effect records — kill-9-durable streams/PEL/DLQ/triggers + WAL recycle plane guard (Wave B stage 2a)#291
pilotspacex-byte merged 7 commits into
mainfrom
feat/wave-b-mq-effect-records

Conversation

@pilotspacex-byte

@pilotspacex-byte pilotspacex-byte commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Wave B stage 2a (task #34) / storage-kernel M1: MQ gets a real durability sub-plane — stream content, consumer-group PEL, DLQ routing, and trigger registrations now survive kill-9.

Root cause of the loss class: MQ.* intercepts before the generic AOF-logging dispatch path, so under --appendonly yes the AOF-authority recovery (db.clear() + rebuild from the AOF manifest) silently discarded every durable stream on every restart. replay_mq_wal couldn't have saved it either: MqAck rolled the PEL back by COUNT (wrong under DLQ over-claim padding or partial ACKs) and every payload hardcoded db 0.

What's in here

  • Five versioned effect records (leading version byte, db-index-carrying, defensive decode): MqCreate 0x70 / MqAck 0x71 bumped in place, new MqPush 0x72 (captures the ASSIGNED id → outcome-deterministic replay), MqPop 0x73 (claimed ids + resulting last_delivered_id + DLQ routing decisions), MqTrigger 0x74 (registration as opaque data — replay re-arms, never fires).
  • Emission through the K1 typed wal_append channel at all 6 owner-shard sites in mq_exec.rs + 3 MQ.PUBLISH TXN-materialization legs (monoio/sharded/spsc).
  • Replay rewritten: strict WAL order, ACK-by-id, per-db, skip-and-warn on malformed or newer-version records; runs after the AOF-authority wipe (order verified by line-trace, not comments).
  • WAL recycle plane guard (adversarial-review P0 fix): every recycler (autovacuum Pass C disk-offload arm, checkpoint recycle_segments_before, admin VACUUM) deleted sealed segments against an LSN floor that covers only KV pages + graph — WS/MQ/temporal have NO snapshot in any mode, so disk-offload deployments under --max-wal-size pressure permanently lost plane history with no crash required. The guard is central in the recyclers: any segment holding a plane record is kept (fail-closed on torn/unknown/unreadable), counted in reclamation_wal_recycle_blocked_no_checkpoint_total, rate-limited warn. Pure-KV segments recycle exactly as before.
  • New fuzz target mq_wal_record (all five decoders, both CI matrices); regression tests proving pre-versioning payload shapes fail closed, including the key_len-low-byte==1 collision cases.

Compatibility

0x70/0x71 payload layout is a deliberate break: MQ WAL writes were dead-wired until PR #286 (2026-07-11) — wal_append_tx was never assigned — so no released binary ever wrote the legacy layout; only ~1-day-old dev builds did, and those payloads fail closed to skip-and-warn (tested).

Known limitation (tracked as task #46 + CHANGELOG caveat)

Durable streams deleted via generic DEL/UNLINK/FLUSHALL are resurrected with full content by replay after restart — pre-existing class (no MQ tombstone record yet), same shape as the already-fixed vector/KV cold-plane resurrection. Follow-up designs an MqDrop tombstone.

Verification

  • Red/green kill-9 crash test tests/crash_recovery_mq_effects.rs: CREATE → PUSH×5 → POP(3) → ACK(2/3) → forced-DLQ pair → TRIGGER, kill -9, restart on same --dir, asserts content + cursor + PEL-by-id + DLQ + trigger re-arm. 3× consecutive green, both runtimes.
  • Plane-guard red/green: recycle tests failed against the unguarded recyclers, green after; autovacuum-level test covers counter wiring; pre-existing disk-offload pure-KV recycle test kept green (no recycle freeze).
  • Adversarial review (independent agent): SHIP-WITH-FIXES — the P0 (recycle) fixed in this PR, P1 (DEL resurrection) tracked as P1-3: streaming cold-tier vector decode (avoid bulk Vec allocation) #46, P2s (legacy-collision tests, bytes/op measurement) addressed. Reviewer confirmed: replay-after-wipe ordering, emission completeness incl. over-claim padding, no unwraps/locks-across-await, tokio parity.
  • macOS: lib 4167/4167, clippy -D warnings default + tokio matrices, fmt.
  • Linux VM (fresh ELF, pinned MOON_BIN): crash suites mq_effects / temporal_mq / wal_recycle_legacy all green, lib 4187/4187.
  • Measured WAL footprint (release): ~68 B per small PUSH, ~132 B per 3-claim POP, 576 B for the whole crash-test lifecycle incl. 64 B segment header.
  • Bench waiver: MQ paths are not the KV hot path; encode happens at owner-shard execution (not dispatch), and no KV dispatch/protocol code is touched.

Scope

Durability plane only. Stage 2b (separate PR): replication emission, replica apply arms, FULLRESYNC registry legs, read-only enforcement for WS/MQ.

Summary by CodeRabbit

  • Bug Fixes
    • Improved durable MQ durability and replay correctness across kill-9 restarts, including restores of queues, pending entries, acknowledgments, dead-letter routing, and trigger registration.
    • Hardened WAL recovery to safely skip malformed/unsupported MQ records without interrupting replay.
    • Added a WAL recycling safety guard to preserve segments required for durable MQ recovery; also fixed WAL-driven durable publish materialization.
  • Tests
    • Added crash-recovery coverage for the full MQ lifecycle.
    • Added fuzz testing for malformed/truncated MQ WAL v3 record decoding.
  • Documentation
    • Updated reclamation INFO output with an additional WAL append-drops metric for durability observability.

Adds three new WalRecordType discriminants (MqPush 0x72, MqPop 0x73,
MqTrigger 0x74) and rewrites src/mq/wal.rs as a versioned envelope codec
for all five MQ mutation kinds (MqCreate, MqPush, MqPop, MqAck, MqTrigger).

MqCreate (0x70) and MqAck (0x71) keep their existing discriminants but move
to a new payload layout: every payload now starts with a version byte and
carries the affected db index explicitly, fixing a pre-existing db-0
hardcode bug. Precedent for changing an on-disk payload layout without
bumping the discriminant: WalRecordType::XactCommit's own doc comment notes
the 0x51->0x53 format freeze — WAL v3 segments are short-lived and rotated
with no cross-version compatibility contract.

Every decoder returns None on a malformed payload OR an unsupported
(future) version byte, never panics — callers are expected to skip-and-warn
rather than abort a replay scan on one bad record. MqPop's payload carries
the full claim set (id + delivery_count per claimed message) plus any DLQ
routing decisions (source id -> assigned DLQ id), so a later replay can
reconstruct the consumer group's PEL and last_delivered_id exactly instead
of approximating via a count-based heuristic.

~35 unit tests cover roundtrips, empty/malformed/truncated payloads, and
unknown-version rejection for all five record kinds.

This is durability-plane scaffolding only; no emission or replay call sites
are wired up yet (next commits).

author: Tin Dang
Wires the new versioned MQ WAL codec (src/mq/wal.rs) into every owner-shard
MQ command handler in src/shard/mq_exec.rs:

- handle_create: MqCreate now carries db_index (was hardcoded to 0).
- handle_push: builds the MqPush payload inside the with_shard_db closure,
  before `fields` is moved into `stream.add`, then emits it after the
  closure returns.
- handle_pop: threads a WAL payload out of every early-return path
  (restructured to return `(Frame, Option<Vec<u8>>)`); records the full
  claimed-id set with delivery counts, the resulting last_delivered_id, and
  any DLQ routing decisions (source id -> assigned DLQ id) so replay can
  reproduce POP's outcome exactly instead of re-deriving it.
- handle_ack: emits one MqAck record per acked id (by id, not by count —
  the replay-side fix landing in the next commit depends on this).
- handle_trigger: emits MqTrigger before the registry insert consumes the
  key/callback bytes.

`wal_append_on_slice` moves from private to `pub(crate)` since a later
commit calls it from the TXN materialization hop (handler_monoio/txn.rs,
handler_sharded/txn.rs, spsc_handler.rs) as well.

No replay-side changes yet — these records land on disk but are not yet
applied on restart (next commit).

author: Tin Dang
…ount

Rewrites replay_mq_wal (src/shard/shared_databases.rs) to apply every MQ
WAL record one at a time, in true on-disk LSN order, via a new
apply_mq_wal_record dispatcher and five apply_mq_* handlers (create, push,
pop, ack, trigger) — replacing the old behavior that collected replayed
state into intermediate maps and rolled the consumer group's PEL back to a
COUNT-based snapshot cursor. That heuristic was wrong by construction: a
POP that padded its claim beyond the client-visible count (MAXDELIVERY > 0
over-claims for DLQ candidates), or a partial ACK of a multi-message claim,
could not be reconstructed from a message count alone.

apply_mq_ack now calls Stream::xack by id (idempotent — a replayed ack that
was already applied, or an id that was never actually pending, is a
harmless no-op). apply_mq_pop rebuilds the PEL entry for every claimed id
with its recorded delivery_count, restores last_delivered_id verbatim, and
replays DLQ routing by looking up each source id's field content from
`stream.entries` (already rebuilt by every MqPush applied so far) before
taking a mutable borrow of `stream.groups`, then re-pushing into the
sibling DLQ stream at its original assigned id. apply_mq_create now creates
the stream in the record's own db_index instead of hardcoding db 0.

Every apply path is idempotent by construction (guarded by
`contains_key`/`xack`'s no-op semantics), so replaying a partially-persisted
segment or restarting twice is safe. Decode failures (malformed payload or
an unsupported future version byte) are skip-and-warned via
warn_skip_mq_record + tracing::warn!, tracked in a new MqReplayStats
counter set logged once per shard — never abort the replay scan.

Unit tests replace the old count-based-rollback test with:
  - test_replay_mq_wal_restores_full_lifecycle_by_id
  - test_replay_mq_wal_dlq_routing_restored
  - test_replay_mq_wal_trigger_registered
  - test_replay_mq_wal_survives_prior_db_clear (pins the ordering invariant:
    replay_mq_wal must run AFTER the AOF-authority db.clear() wipe, which
    was already true in main.rs's call order but had no regression test)
  - test_replay_mq_wal_unknown_version_skipped_not_fatal

author: Tin Dang
MQ.PUBLISH materializes its queued intents at TXN commit time on two
separate legs — the self-fold path (the committing connection's own shard
owns the queue) and the foreign-shard path (a ShardMessage::MqCommand hop
to the owning shard) — each duplicated across the monoio and tokio
connection handlers. Neither leg emitted a WAL record for the resulting
stream entry, so a durable queue populated exclusively via MQ.PUBLISH
inside a MULTI/EXEC block had no durability at all, independent of the
mq_exec.rs handle_push fix (previous commits) which only covers the direct
MQ.PUSH command path.

Fixes all four call sites:
  - src/server/conn/handler_monoio/txn.rs (self-fold leg)
  - src/server/conn/handler_sharded/txn.rs (tokio self-fold leg)
  - src/shard/spsc_handler.rs (MqTxnMaterialize handler, shared by both
    runtimes' foreign-shard leg)

Each site builds the MqPush payload inside the same with_shard_db closure
that assigns the message id and calls `stream.add`, collects the payloads,
then emits them via mq_exec::wal_append_on_slice after the closure returns
— same pattern as handle_push in mq_exec.rs.

author: Tin Dang
…lity

Adds tests/crash_recovery_mq_effects.rs::mq_effect_records_survive_kill9,
the RED/GREEN proof for the whole Wave B stage 2a change: under
`--appendonly yes`, exercises CREATE -> PUSH x5 -> POP(3) -> ACK(2 of 3) on
one queue, a second CREATE/PUSH x2/POP(2) pair on another queue that forces
immediate DLQ routing (MAXDELIVERY 1), and a TRIGGER registration on a
third queue; kills -9 the server; restarts on the same --dir; and asserts
stream content (XLEN/XRANGE field values), the consumer-group delivery
cursor (POP resumes at msg 4, not a re-delivery of msg 1..3), the PEL
restored by id (XPENDING reports exactly msg 3 pending, not all 3 claimed
messages), DLQ routing (MQ DLQLEN), and trigger re-arming (a fresh PUSH
notifies mq:trigger:<key> with the original callback payload) all survive.
Pre-fix, every one of these assertions fails: the AOF-authority db.clear()
wipes the keyspace on restart and nothing replayed it back.

Measured WAL footprint (release binary, single shard, --appendonly yes):
CREATE + 5xPUSH + POP(3) = 7 effect records total 576 bytes on disk
(64-byte v3 segment header + records), average ~82 bytes/record — 68 bytes
per PUSH (48-byte payload + 20-byte v3 framing/CRC overhead), ~132 bytes
for a 3-claim POP (112-byte payload + framing).

Adds fuzz/fuzz_targets/mq_wal_record.rs (registered in fuzz/Cargo.toml and
both fuzz-pr/fuzz-nightly matrices in .github/workflows/fuzz.yml), fuzzing
all five MQ WAL decoders directly against attacker/corruption-controlled
bytes — every decoder is contracted to return None on malformed or
unsupported-version input, never panic or read out of bounds.

Updates tests/crash_recovery_temporal_mq.rs's module doc comment (which
previously explained why a live MQ round trip was NOT possible) to point at
the new test now that the gap is closed, and fixes two stale references to
a unit test name that this change's replay rewrite superseded.

CHANGELOG.md [Unreleased] entry added per the Lint gate's requirement.

author: Tin Dang
…/MQ/temporal history

Adversarial review of the stage-2a branch (verdict SHIP-WITH-FIXES)
confirmed a P0: every WAL recycler — autovacuum Pass C in disk-offload
mode, the checkpoint protocol's recycle_segments_before, and the admin
VACUUM path — deletes sealed segments against an LSN floor that only
covers KV pages and the graph store. The workspace/MQ/temporal planes
have no snapshot format in ANY mode, so a disk-offload deployment under
--max-wal-size pressure permanently lost MQ/WS/temporal history during
normal operation — no crash required. Stage 2a raised the blast radius
from queue definitions to full stream content/PEL/DLQ/trigger history.

Fix is central, in the recyclers themselves (kernel principle: recycle
takes the min across planes): segment_holds_plane_history() walks a
sealed segment's record-type bytes and any segment holding a plane
record is skipped by BOTH recycle_aggressive and
recycle_segments_before, regardless of caller. Fail-closed: unreadable
file, non-v3 header, unknown (future) record type, or torn tail all
keep the segment; a record_len==0 tail is normal zero-padding. Blocked
segments are reported via RecycleStats.segments_blocked_plane; the
autovacuum disk-offload arm feeds them into
reclamation_wal_recycle_blocked_no_checkpoint_total with a rate-limited
warn. Pure-KV segments recycle exactly as before (covered by the
pre-existing disk-offload test, kept green).

Also from the review round:
- P2 regression tests: pre-versioning MqCreate/MqAck payloads (written
  only by dev builds between PR #286 and this branch) fail closed under
  the new versioned decoders, including the key_len-low-byte==1
  collision shapes (1- and 257-byte keys).
- P1 filed as task #46 + CHANGELOG caveat: durable MQ streams deleted
  via generic DEL/UNLINK/FLUSHALL are resurrected with full content by
  replay — needs an MqDrop tombstone plane (same class as the fixed
  vector/KV cold-plane resurrection).
- CHANGELOG: measured WAL footprint added (~68 B/small PUSH, ~132 B per
  3-claim POP, 576 B for the crash-test lifecycle).

Red/green: test_recycle_aggressive_keeps_plane_history_segments and
test_recycle_segments_before_keeps_plane_history_segments failed against
the unguarded recyclers (compile-red on the stats field, then
assertion-red), green after;
test_pass_c_disk_offload_keeps_plane_segments_and_counts_blocked covers
the autovacuum wiring + counter.

author: Tin Dang
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 46c889af-e999-49e6-a321-96aa14d219f2

📥 Commits

Reviewing files that changed from the base of the PR and between 2af7089 and a6c8d11.

📒 Files selected for processing (2)
  • src/command/info_reclamation.rs
  • src/shard/mq_exec.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/shard/mq_exec.rs

📝 Walkthrough

Walkthrough

MQ operations now emit versioned WAL v3 effect records, replay reconstructs durable queue state deterministically, WAL recycling preserves sole-copy plane history, and crash-recovery plus decoder fuzz coverage validates the changes.

Changes

MQ WAL durability

Layer / File(s) Summary
Versioned MQ WAL contracts
src/mq/wal.rs, src/persistence/wal_v3/record.rs, src/persistence/wal_v3/replay.rs, fuzz/*, .github/workflows/fuzz.yml
MQ payloads include version and database context, new MQ record types are recognized, malformed payloads fail closed, and decoder fuzzing is registered in PR and nightly workflows.
Owner-side MQ WAL emission and observability
src/shard/mq_exec.rs, src/server/conn/.../txn.rs, src/shard/spsc_handler.rs, src/command/info_reclamation.rs
MQ mutations and transaction materialization append deterministic WAL effect records, while dropped WAL channel enqueues are counted, logged, and exposed through reclamation output.
MQ replay reconstruction and crash validation
src/shard/shared_databases.rs, tests/crash_recovery_mq_effects.rs, tests/crash_recovery_temporal_mq.rs, CHANGELOG.md
Replay restores stream entries, consumer PEL state, cursors, DLQ routing, acknowledgements, and trigger registrations while skipping unsupported records; crash-recovery coverage exercises restart behavior.
Plane-history WAL recycling guard
src/persistence/wal_v3/segment.rs, src/shard/autovacuum.rs
Segments containing sole-copy plane history are preserved during recycling, counted, logged, and covered by recycling and autovacuum tests.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant OwnerShard
  participant WALv3
  participant Replay
  participant MQState

  Client->>OwnerShard: Execute MQ command
  OwnerShard->>WALv3: Append versioned MQ effect record
  WALv3->>Replay: Read records after restart
  Replay->>MQState: Rebuild stream and consumer state
  MQState-->>Client: Serve restored MQ state
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main durable-MQ WAL and recycle-guard changes.
Description check ✅ Passed The description is detailed and on-topic, but it does not follow the template’s Checklist, Performance Impact, and Notes sections.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/wave-b-mq-effect-records

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/persistence/wal_v3/record.rs (1)

531-534: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add the new discriminants to the from_u8 roundtrip loop.

The enum→u8 asserts at Lines 526-528 cover MqPush/MqPop/MqTrigger, but the u8→enum roundtrip loop still stops at 0x71, so the three new from_u8 arms (Lines 124-126) are never exercised.

💚 Include the new tags in the roundtrip loop
         for &v in &[
             0x01, 0x10, 0x20, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x40, 0x41, 0x42, 0x50,
-            0x52, 0x53, 0x60, 0x61, 0x70, 0x71,
+            0x52, 0x53, 0x60, 0x61, 0x70, 0x71, 0x72, 0x73, 0x74,
         ] {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/persistence/wal_v3/record.rs` around lines 531 - 534, Extend the u8→enum
roundtrip loop in the relevant record tests to include the discriminants for
MqPush, MqPop, and MqTrigger, matching their new from_u8 arms and the existing
enum→u8 assertions. Keep the current loop structure and all existing tags
unchanged.
🧹 Nitpick comments (1)
src/persistence/wal_v3/segment.rs (1)

129-175: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Scanning the full segment via fs::read is wasteful on the recycle hot path.

segment_holds_plane_history loads the entire segment (payloads included) into memory, but only needs the per-record framing (record_len + the type byte at offset+12). For a pure-KV segment it reads to EOF every time, and because plane-heavy WALs deliberately stay above --max-wal-size, Pass C keeps firing every ~30s and re-reads the full WAL set on each tick. Consider a BufReader that reads the 12-byte framing prefix and seeks past each payload, early-returning on the first plane record.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/persistence/wal_v3/segment.rs` around lines 129 - 175, Update
segment_holds_plane_history to avoid fs::read and full-segment buffering: open
the path with a buffered reader, inspect each record’s framing prefix to obtain
record_len and the type byte at offset+12, then seek past the remaining payload
before continuing. Preserve the existing malformed, unknown-type,
invalid-header, and read-error fail-closed behavior, while returning immediately
when a plane record is found or a zero-padded tail is reached.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server/conn/handler_monoio/txn.rs`:
- Around line 170-175: Update the WAL append flow around wal_append_on_slice in
the monoio transaction handler and its identical handler_sharded path so
try_send failures are observable rather than discarded. Propagate or explicitly
handle full/disconnected channel errors with backpressure or a defined
transaction-failure policy, ensuring the commit is not acknowledged until the
MqPush record is successfully enqueued.

In `@src/server/conn/handler_sharded/txn.rs`:
- Around line 177-182: Update the transaction handling around
wal_append_on_slice so WAL enqueue failures are observable rather than ignored
after stream mutation. Propagate or explicitly handle full/disconnected channel
errors, applying the existing durability/backpressure policy before reporting
commit success, and ensure failed MqPush enqueue cannot be treated as a
successful commit.

---

Outside diff comments:
In `@src/persistence/wal_v3/record.rs`:
- Around line 531-534: Extend the u8→enum roundtrip loop in the relevant record
tests to include the discriminants for MqPush, MqPop, and MqTrigger, matching
their new from_u8 arms and the existing enum→u8 assertions. Keep the current
loop structure and all existing tags unchanged.

---

Nitpick comments:
In `@src/persistence/wal_v3/segment.rs`:
- Around line 129-175: Update segment_holds_plane_history to avoid fs::read and
full-segment buffering: open the path with a buffered reader, inspect each
record’s framing prefix to obtain record_len and the type byte at offset+12,
then seek past the remaining payload before continuing. Preserve the existing
malformed, unknown-type, invalid-header, and read-error fail-closed behavior,
while returning immediately when a plane record is found or a zero-padded tail
is reached.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a5ef634e-38b3-4d68-ad06-c3c38ac6667e

📥 Commits

Reviewing files that changed from the base of the PR and between 29a3480 and 2af7089.

⛔ Files ignored due to path filters (1)
  • fuzz/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (16)
  • .github/workflows/fuzz.yml
  • CHANGELOG.md
  • fuzz/Cargo.toml
  • fuzz/fuzz_targets/mq_wal_record.rs
  • src/mq/wal.rs
  • src/persistence/wal_v3/record.rs
  • src/persistence/wal_v3/replay.rs
  • src/persistence/wal_v3/segment.rs
  • src/server/conn/handler_monoio/txn.rs
  • src/server/conn/handler_sharded/txn.rs
  • src/shard/autovacuum.rs
  • src/shard/mq_exec.rs
  • src/shard/shared_databases.rs
  • src/shard/spsc_handler.rs
  • tests/crash_recovery_mq_effects.rs
  • tests/crash_recovery_temporal_mq.rs

Comment thread src/server/conn/handler_monoio/txn.rs
Comment thread src/server/conn/handler_sharded/txn.rs
CodeRabbit (PR #291, both txn.rs call sites): wal_append_on_slice used
try_send and discarded the result, so a full/disconnected channel could
silently drop an MqPush record AFTER the stream mutation was applied —
TXN.COMMIT succeeds, restart recovery loses the message, nobody knows.

Blocking is not an option here: every caller runs ON the shard thread,
which is also the channel's sole consumer (1ms-tick drain in
event_loop.rs) — waiting on a full channel would deadlock the very drain
that empties it. Overflow requires >4096 records in a single tick, and
the mutation cannot be unwound mid-commit, so the correct policy is
observability: on try_send failure emit tracing::error! and increment
the new reclamation_wal_append_channel_dropped_total INFO counter
(# Reclamation section, field count 29 → 30 with test updated).
Operators alert on any increase; zero means the durability guarantee
held. Failure-policy rationale documented on the helper.

author: Tin Dang
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.

2 participants