Repository navigation
fix(TC-1317): bound the read-audit wait and skip abandoned records - #350
Merged
Merged
Conversation
A read waits at most database.read_audit_wait_ms (20 s) for its audit record, then answers 503 read_audit_busy without its data. Records whose reader was already gone when their batch formed are skipped and counted, so a backlog drains on its own; no read's data is returned unless its record committed. Transient batch failures are 503 unavailable_retryable, infrastructure errors are no longer 401, and a panic fails only its batch. A watchdog logs stalled, slow, failed and expired audits, and Prometheus gains failure, abandon and busy counters plus queue and in-flight gauges.
Contributor
Author
|
REQUEST_CHANGES: see TC-1317 (review of head |
… into skgbafa/tc-1317-read-audit-bound
- R1: the test-only panic fault is per pipeline, not a process-global static that another test's drain could consume. - R2: Rocket-level tests with the read audit held: /signed/kv, SQL and /encryption/networks answer 503 read_audit_busy (not 401) and SQL never executes; a stranger's read is still 401/403; a deadline shorter than the wait answers request_timeout. - The node refuses to start with database.read_audit_wait_ms = 0. - A dead drain is counted (stopped) and reported as a stalled drain; a batch waiting on the SQLite writer lock counts as in flight. - Tests: condition-based sync instead of a fixed sleep; commit-semantics pipeline tests use a long wait so host load cannot turn them busy.
… into skgbafa/tc-1317-read-audit-bound # Conflicts: # .github/workflows/docker.yml # docs/releasing.md # docs/runbooks/tc-959-n0-log-shipping.md
Contributor
Author
|
APPROVE: see TC-1317 (re-review of head |
samgbafa
changed the base branch from
skgbafa/tc-1274-sql-wait-bounds
to
main
October 19, 2026 11:11
Merged
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.
Refs TC-1317. Stacked on #347 (base
skgbafa/tc-1274-sql-wait-bounds); it uses #347'srequest_cancelstage. Merge after 1.21.0, after #347. It also depends on tinycloud-dev#154 (read-audit event codes, stacked on #147);log-shipper/canonical.jsonpins #154's head4adaf13and needs re-pinning once it lands.Every authorized read waits for its invocation record to commit before its data is returned. That covers KV get, list, metadata and sync, capabilities read, and the authorization step of SQL, DuckDB, space-info,
/signed/kvand/encryption/networks. The wait had no bound, and one serial queue serves every account. The Oct 14 all-account read hang fits a self-sustaining backlog of that queue [INFERENCE, TC-1274 05eb8962]. The design was approved by Naomi in~/scratch/tc1274/read-audit/DESIGN.md.Changes
database.read_audit_wait_ms, 20 s by default, covers both queue admission and the receipt. On expiry the read gets503 read_audit_busy: the read audit is backlogged; retry the requestwithRetry-After: 5, and no data. A request cut by the/invokedeadline while it waits now logsstage="read_audit".Sender::is_closed()) and counts them inabandoned.503 unavailable_retryable.verify_authand the encryption routes no longer report infrastructure errors as 401.catch_unwind) fails only that batch; the drain keeps running.Read audit wait expired→read_audit_busy;Read audit batch slow(over 5 s) →read_audit_batch_slow;Read audit batch failed(with a fixederror_class: a transient class,otherorpanic) →read_audit_batch_failed;Read audit drain stalled(a batch in flight over 30 s, or queued records with no progress for 10 s) →read_audit_drain_stalled.tinycloud_read_audit_failed_batches_total,…_abandoned_total,…_busy_total;tinycloud_read_audit_queue_depthandtinycloud_read_audit_in_flight_batch_age_ms.read_audit_commit_statsis replaced byread_audit_health().VECTOR_CONFIG_JSONis regenerated, so the N0 raw lock moves to966199dc…(docker.ymln0_hash, the N0 fixture,docs/releasing.md, the TC-959 runbook)./signed/kvand/encryption/networks(TC-1418). Their read-audit wait is now bounded by the core bound; adding the deadline needs per-route write latches, so I filed it rather than wrap them here.Tests
tc1317_a_held_drain_answers_busy_within_the_boundfailed;tc1317_abandoned_records_are_skipped_and_the_drain_recovershung, because the wait was unbounded;tc1317_a_panicking_batch_does_not_stop_the_drainfailed;tc1317_a_transient_batch_failure_is_retryablefailed;tc1317_a_held_read_audit_bounds_reads_and_spares_writesfailed.tc1317_no_read_is_confirmed_without_its_committed_record: 48 concurrent waiters, a third of them cut. Every confirmed read has its row, and records committed plus abandoned equals 48.tc1317_the_watchdog_reports_a_stalled_drain_once_per_interval.a_kv_read_with_a_stalled_read_audit_is_answered_busy: a real/invokeKV get answers 503read_audit_busywithout the value, then serves it once the drain frees up.read_audit_busyin the fairing test.postgres_read_audit_failed_batch_commits_nothing_and_fails_every_waiternow expects a terminated connection to reach waiters asunavailable_retryable, not the raw driver text.Runs (local, native PostgreSQL 18, no Docker):
cargo fmt --check, plus clippy for the workspace (-Dwarnings) and with--features duckdb: clean.cargo test -p tinycloud-corelib: 490 pass. With--features duckdb: 535 of 536 pass. The one failure was fix(TC-1274): bound SQL waits and add an /invoke deadline #347'stransient_storage_errors_are_unavailable_and_release_the_lock, its known load flake, at host load average ~20 to 30. It passed in 40 isolated runs, 30 parallelsql::serviceruns and 6 more full-binary runs.cargo test -p tinycloud-nodewithTINYCLOUD_TEST_BLACKHOLE=1: all pass, except 3policy_v3::account_alias_teststhat hit their 10 s fixture timeouts in one run at high load. They passed alone and in 3 more full lib runs (537 of 537).tc959-check --canonicalpasses;node --testgives 33 pass;scripts/ci/test-*.pyall pass.Smoke (a real node on PostgreSQL,
read_audit_wait_ms=3000, through the SDK).LOCK TABLE invocation IN EXCLUSIVE MODElets reads through but blocks audit inserts.503 read_audit_busyin about 3.1 s.Read audit wait expired(count 1, rate-limited);Read audit drain stalled(in flight 15.9 s, queue 5);Read audit batch slow(30 001 ms) andRead audit batch failedwitherror_class="sqlstate_57014".