feat: count failed sends and rejected or dropped inbound frames - #165
Open
jacderida wants to merge 1 commit into
Conversation
V2-834 Part C.5. The V2-623 `wire traffic summary` counted only confirmed sends and successfully decoded receives, so neither side summed to what the transport actually moved. Three failure branches are now itemised on `TrafficCounters`: - `wire_failed_tx_*`: `send_on_channel` send errors and identity-announce send errors (both sub-branches, including the PeerNotFound early return). - `wire_rejected_rx_*`: frames that reached a shard consumer but failed postcard decode or ML-DSA verification (both collapse to `None` in `parse_protocol_message`). - `wire_dropped_rx_*`: frames the dispatcher dropped before parsing because the shard channel was full or its consumer had exited — previously count-only via `drop_counter`, byte length now recovered from the `TrySendError` payload. `wire_rx_bytes + wire_rejected_rx_bytes + wire_dropped_rx_bytes` is every frame the transport handed to saorsa-core. All six fields are added to the periodic INFO line (19 fields, under the 32-field cap). Test evidence: `cargo clippy --lib --bins -- -D warnings` clean, `cargo fmt --check` clean. The test-target clippy error at `network.rs:2997` is pre-existing on main. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
dirvine
approved these changes
Sep 16, 2026
dirvine
left a comment
Member
There was a problem hiding this comment.
Approving. Small, clean, observability-only: 6 counters partition the failure branches so tx/rx sum by construction (failed_tx on send failures, rejected_rx on decode/ML-DSA verify failure, dropped_rx on dispatcher Full/Closed). The partition wire_rx + rejected + dropped = every frame handed to saorsa-core is sound. Emits on the existing periodic wire-traffic line, under the 32-field cap. No concerns. Note: Security Audit CI failure is pre-existing baseline (unmaintained/yanked transitive advisories), not introduced here.
mickvandijke
added a commit
to mickvandijke/saorsa-core
that referenced
this pull request
Sep 21, 2026
…nomi#165) Add ADR-0005 capturing the design decisions behind the replication repair hardening work: responsibility decided at download against live routing state, fresh offers off the serial loop, detached async/LMDB lifecycle tracking, terminal closed streams, eager neighbor-sync drain, TTL'd bootstrap accounting, source-aware bounded verification, retry reservation, O(1) hint merge, and shared audit coordination. Status: Proposed. Passes scripts/adr-governance.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mickvandijke
added a commit
to mickvandijke/saorsa-core
that referenced
this pull request
Sep 21, 2026
ADR-0005 asserted "SemVer: patch. No wire-format or public-API change". The wire-format half is true and verified: REPLICATION_PROTOCOL_ID stays at v2 and no ReplicationMessageBody variant or field changes (the only protocol.rs edit in the branch widens a private const to pub(crate)). The public-API half is false. src/lib.rs exposes `pub mod replication`, and the branch breaks it: audit_tick and run_prune_pass removed, AuditTickResult::Failed and PrunePassContext and VerificationTargets gained required fields, VerificationEntry and FetchCandidate restructured (FetchCandidate also losing its Ord/PartialOrd/Eq impls to the new FetchOrder), MAX_PENDING_VERIFY_PER_PEER and MAX_PRUNE_AUDIT_CHALLENGES_PER_PASS removed, plus pending_count_for_sender and evict_stale changes. Replace the claim with a Compatibility bullet that states both halves accurately and enumerates every break against the decision that forced it, and record the absence of compatibility shims as a decision: the changed items are the data structures this ADR restructures, so a parallel deprecated surface would pin the old representation in place. Carries no versioning or release semantics — the required version position is the release manager's call, not the ADR's. This also drops the same stale "patch-level" claim from the Constraints section, which stated it a second time. Raised as blocking issue 3 in review of PR WithAutonomi#165. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mickvandijke
added a commit
to mickvandijke/saorsa-core
that referenced
this pull request
Sep 21, 2026
Review of PR WithAutonomi#165 asked for the possession-check scheduler backlog to be bounded or coalesced. Bounding it is the wrong move and the ADR now says why, so the decision survives the next reader who notices the unbounded channel. The queue is fed from exactly one place: a FreshWriteEvent this node accepted and fanned out. Every entry is therefore a chunk this node took a client PUT for and was paid for — nobody inflates it without paying per chunk and having this node accept it. That is categorically unlike the inbound serial queue of decision 12, where a stranger's bytes size the queue, which is why a bound is right there and wrong here. A cap would also discard what the queue is for: a dropped possession check is a peer that failed to store going unpunished. Memory does not motivate one either — ~224 bytes plus task overhead per event, steady state being arrival rate times the ~10 minute settle window, so ~6k parked tasks at a sustained 40 MB/s of accepted ingest. What is genuinely unbounded is lateness. A single-key probe against a per-target limit of 2 yields ~40 probes/s from a healthy peer but ~0.45/s from one burning the full 4.4s deadline, so a backlog forms only when peers are already timing out — and a late verdict is a wrong verdict, since neighbor sync may have delivered the chunk by then. A count cap does not address that; it discards a different arbitrary subset. Records the cancellability fix that did matter, the two rejected alternatives (capping the queue; closing the coordinator semaphore, which would report MalformedResponse against innocent peers on every shutdown), and coalescing per target as the deferred real fix — AuditChallenge.keys is already a Vec answered by per-key digests, and audit_response_timeout(key_count) already gives the batch a principled ceiling. Re-open trigger is probe lateness, not queue depth. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mickvandijke
added a commit
to mickvandijke/saorsa-core
that referenced
this pull request
Sep 21, 2026
MAX_FRESH_OFFER_ATTEMPTS_PER_KEY gated on `pending.len()`, the queue's instantaneous depth. The handler pops a proof before verifying it, so every pop returned a slot that a fresh source could refill. Since the per-source set only bars repeats, a stream of distinct peers kept one entry alive indefinitely: unbounded sequential payment verifications — EVM and DHT work — while holding an admission permit and one of only four fresh-offer worker slots. Four such keys idle the whole pool. The staleness shed is no backstop; it runs once before the loop, not inside it. Count admissions instead, and never decrement. A popped proof has spent its slot rather than returned it, so a key costs at most CLOSE_GROUP_MAJORITY verifications no matter how many peers offer it. This also bounds the entry's source set, which previously grew one PeerId per sybil for the life of the entry. Regression test drives pop-then-admit with a fresh source each time and asserts the lifetime count holds; it accepted 16 proofs against a budget of 4 before the fix. Reported by AI review of PR WithAutonomi#165, independently reproduced here before fixing. Verified with the commands CI runs, including the no-default-features build and test that the previous commit tripped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mickvandijke
added a commit
to mickvandijke/saorsa-core
that referenced
this pull request
Sep 21, 2026
…nc-drain-window fix(replication): drain priority sync queue and recover from routing-event lag
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.
Summary
V2-834 Part C.5.
The V2-623
wire traffic summarycounted only confirmed sends andsuccessfully decoded receives, so neither side summed to what the
transport actually moved. Three failure branches are now itemised on
TrafficCounters:wire_failed_tx_*:send_on_channelsend errors and identity-announcesend errors (both sub-branches, including the PeerNotFound early
return).
wire_rejected_rx_*: frames that reached a shard consumer but failedpostcard decode or ML-DSA verification (both collapse to
Noneinparse_protocol_message).wire_dropped_rx_*: frames the dispatcher dropped before parsingbecause the shard channel was full or its consumer had exited —
previously count-only via
drop_counter, byte length now recoveredfrom the
TrySendErrorpayload.wire_rx_bytes + wire_rejected_rx_bytes + wire_dropped_rx_bytesis everyframe the transport handed to saorsa-core. All six fields are added to
the periodic INFO line (19 fields, under the 32-field cap).
Linear issue
Closes V2-834
Risk tier
Counters and one extended INFO line; no behaviour change.
Compatibility
TrafficCountersispub(crate); only thewire traffic summaryINFO line gains six fields)Semver impact
Test evidence
cargo clippy --lib --bins -- -D warnings: clean (the test-targetnetwork.rs:2997empty-line-after-doc lint is pre-existing onmain)cargo fmt --check: cleanNew dependency
none
ADR
n/a
Mitigation / rollback
Revert the PR. Purely additive counters and INFO summary lines at the existing 300s cadence; no config, no wire, no storage change — nothing to migrate.
🤖 Generated with Claude Code