docs(concurrency): document unbounded-channel backpressure invariants#306
Open
githubrobbi wants to merge 1 commit into
Open
docs(concurrency): document unbounded-channel backpressure invariants#306githubrobbi wants to merge 1 commit into
githubrobbi wants to merge 1 commit into
Conversation
Phase 10d audit of all true prod unbounded `mpsc::unbounded_channel()`
call sites. Phase-10a baseline reported 4 (3 daemon + 1 client) based
on raw regex hits; hand-audit corrected to **2 true prod sites**:
* `crates/uffs-daemon/src/cache/journal_sink.rs:160` (`RegistryPatchSink::apply_tx`)
* `crates/uffs-client/src/connect.rs:83` (`UffsClient::notification_tx`)
The script's "3 daemon" matched two field-type sigs + the constructor
inside the same file plus a `#[cfg(test)] new_for_test` constructor;
two additional `uffs-daemon/src/index/search.rs` matches were
doc-comment occurrences of the word "unbounded" (predicates /
display-row filters).
Both prod sites are kept unbounded with documented "by-construction
bounded" rationale + producer-rate ceilings + worst-case memory
calculations.
## Site 1 — `RegistryPatchSink::apply_tx`
Sync callback escape hatch from the journal loop's
`accept`/`trigger_save`/`journal_wrapped` (sync `fn`, not `async fn`)
into the async `applier_task`.
Constraints pinning unbounded:
1. Producer is sync-non-blocking by contract — cannot `.await`
on a bounded send. A bounded variant would be `try_send` +
drop-on-full, identical to the existing "dead applier silently
absorbed" degraded path documented on `apply_tx`.
2. Producer cadence throttled upstream by `SaveTrigger` (50K
events ∨ 5-min age). Worst-case steady-state ≈ 5 messages/min
across all drives.
3. Payload bounded (`ApplyMsg::Save` carries ≤ 50K-event
`Vec<FileChange>`, ~10 MB peak, consumed in ~1 s by serial
applier).
Added a full `# Backpressure` rustdoc block on `spawn_with_applier`.
## Site 2 — `UffsClient::notification_tx`
Per-client receive buffer for incoming `DaemonEvent` notifications
forwarded from the IPC read loop.
Constraints pinning unbounded:
1. Producer rate is upstream-bounded by the daemon's
`broadcast::Sender` capacity (`DEFAULT_BROADCAST_CAPACITY`). A
slow client cannot induce unbounded daemon emit.
2. Notification cadence is intrinsically low (≈ 2/min steady-state;
≈ 0.5/sec peak during multi-drive load).
3. `try_send` on bounded would invert responsibility — the producer
is the async IPC `read_loop`, and stalling it would block RPC
responses on the same socket, strictly worse than the current
"drop only at the daemon broadcast layer" design.
Added a full `# Backpressure` rustdoc block on the
`notification_tx`/`notification_rx` field docs.
## Rule-1 adherence
* Zero `#[allow(...)]` introductions.
* No suppression hacks, no skipped tests.
* Documentation-only — zero behavior change.
* `cargo check / clippy -D warnings / fmt` all clean.
* `cargo test -p uffs-daemon --lib` — 298 passed.
* `cargo test -p uffs-client` — 2 passed + 1 doctest.
Per-site verdict in
`docs/dev/baseline/2026-05-19/phase_10_backpressure_audit.md` (local).
Refs #302.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Phase 10d audit closure — document the 2 true prod unbounded
mpsc::unbounded_channel()call-sites as deliberately unbounded with rate-bounded producers and explicit memory worst-case ceilings.Why
Phase-10a baseline reported "4 unbounded channels (3 daemon + 1 client)" based on raw audit-script regex hits. Hand-audit corrected the count to 2 true prod sites:
crates/uffs-daemon/src/cache/journal_sink.rs:160(RegistryPatchSink::apply_tx)crates/uffs-client/src/connect.rs:83(UffsClient::notification_tx)The script's "3 daemon" matched two field-type sigs + the prod constructor + a
#[cfg(test)] new_for_testconstructor all in the same file; the additionalindex/search.rshits were doc-comment occurrences of the word "unbounded". These are noise; the corrected per-site verdict is captured indocs/dev/baseline/2026-05-19/phase_10_backpressure_audit.md(local;docs/dev/baseline/is gitignored).Verdict — both sites kept unbounded, justified
RegistryPatchSink::apply_tx(daemon)Sync callback escape hatch from the journal loop's
accept/trigger_save/journal_wrapped(syncfn, notasync fn) into the asyncapplier_task.Constraints pinning unbounded:
.awaiton a bounded send. A bounded variant would betry_send+ drop-on-full, operationally identical to the existing "dead applier silently absorbed" degraded path documented onapply_tx.SaveTrigger(50K events ∨ 5-min age). Worst-case steady-state ≈ 5 messages/min across all drives.ApplyMsg::Savecarries ≤ 50K-eventVec<FileChange>, ~10 MB peak, consumed in ~1 s by the serial applier.Augmented: full
# Backpressurerustdoc block onRegistryPatchSink::spawn_with_applier.UffsClient::notification_tx(client)Per-client receive buffer for incoming
DaemonEventnotifications forwarded from the IPC read loop.Constraints pinning unbounded:
broadcast::Sendercapacity (DEFAULT_BROADCAST_CAPACITY). A slow client cannot induce unbounded daemon emit.try_sendon bounded would invert responsibility — the producer is the async IPCread_loop, and stalling it would block RPC responses on the same socket, strictly worse than the current "drop only at the broadcast layer" design.Augmented: concise inline comment at the
unbounded_channel()call-site referencing the audit doc. Kept tight becauseconnect.rsis right at the 800-LOC file-size policy ceiling — the audit doc carries the full reasoning.Rule-1 adherence
#[allow(...)]introductions.connect.rsat exactly 800 LOC by using a single-line inline comment + audit-doc reference instead of in-source rustdoc.Verification
Local:
cargo fmt -p uffs-client -p uffs-daemon— clean.cargo clippy -p uffs-client -p uffs-daemon --all-targets -- -D warnings— 0 warnings.cargo test -p uffs-daemon --lib— 298 passed / 0 failed.cargo test -p uffs-client— 2 passed + 1 doctest passed.lint-fast— all 7 stages passed.lint-pre-push— all 17 stages passed in 100 s.Acceptance against playbook §1142-1146
✅ CLOSED for Phase 10d. Both prod unbounded channels have producer-rate ceilings documented at the call-site; the audit-script over-count is corrected in
phase_10_backpressure_audit.md; the script's matching regex stays as a "broad sweep" while this hand-audit is the canonical channel inventory.Will be folded into
concurrency_policy.md §"Channel discipline"in Phase 10g.Tracking
Refs #302 (Phase 10 umbrella). Sub-phases queued:
concurrency_policy.md+ per-crate# Concurrencyrustdoc.