Gates / frontend (push) Successful in 1m18s
Gates / test (push) Successful in 2m46s
Gates / test-aarch64 (push) Successful in 7m33s
Gates / package (push) Successful in 5m34s
Gates / container (push) Successful in 17s
CI / gates (push) Successful in 16m16s
Gates / frontend (push) Successful in 1m8s
Gates / container (push) Successful in 9s
Release / gates (push) Successful in 9m15s
Release / guard (push) Successful in 19s
Gates / test (push) Successful in 1m34s
Gates / test-aarch64 (push) Successful in 6m46s
Gates / package (push) Successful in 39s
Release / publish (push) Failing after 4m7s
3.0 KiB
3.0 KiB
Query-log batching: one transaction per interval, not per query
Measured on the deployed Pi: ~0.5 GiB/day of process writes to persist ~2.3 MB/day of query rows, because the writer's 100 ms batch window degenerates to one transaction per query at household rates (~0.25 qps). Write wear killed the previous SD card. Fix: widen the window, Pi-hole precedent (FTL DBinterval=60).
Contract
- New config field
logging.query_log_flush_interval_s: u16, default 60, valid 0–3600, dedicated validation classification (not folded into an existing one).0means: do not wait for more entries; immediately flush the entry plus whatever is already queued, up toflush_batchrows. It is NOT per-query power-loss durability —synchronous=NORMALnever promised that; say so in the reference.
- Writer loop (
src/storage/logger.zig): block ongetOne, then deadline = now + interval on the.bootclock (matching upstream history;.awakewould stretch the window across suspend), fill until deadline orflush_batch(100) rows, one transaction per flush; if the queue holds more, keep flushing inflush_batchchunks. - Shutdown (the Codex-found race, fix required): today
Logger.shutdowncloses the queue and the app then cancels the task group containing the writer, so cancellation can beat the close-observation and drop the held batch. New order: stop and join query producers first, close the queue, await the writer outside the cancelled group (give the writer a lifetime separate from the producers' group). If the disk gate forbids the final write, drain and count the entries as dropped — never hang shutdown, never lose them uncounted. - Unchanged on purpose: queue cap
query_log_buffer_max+ drop-oldest backpressure,flush_batch, pragmas, wal_autocheckpoint, retention, domains interning. One commit per minute makes those second-order.
Propagation (config field checklist)
Model key + round-trip drift guards, validation + validation reference, settings API view (src/web/handlers/settings.zig), openapi.yaml, admin types + settings control, contract samples regenerated, docs config reference, CHANGELOG.
Documentation wording
- Crash-loss window: up to
intervalseconds of query history on process failure; power loss can additionally lose recent committed transactions (WAL + synchronous=NORMAL). Query history is the least valuable data on the box. - Staleness: every query-log-backed read (query-log page, totals, timeseries) lags up to
intervalseconds. The live view is unaffected — it is fed from the hub before the queue.
Acceptance
- Deterministic tests: interval batching (entries within the window land in one transaction), flush_batch early flush, 0-sentinel immediate flush, shutdown drains a held batch and the queue (the race sequence: producers stopped → queue closed → writer awaited), disk-gated final drain counts drops.
- Suites:
zig build testand-Dintegration0 failed; admin typecheck/vitest/prettier clean; goldens regenerated.