• v0.0.7 324704b53f

    v0.0.7
    Gates / package (push) Successful in 41s
    Gates / test (push) Successful in 1m35s
    Gates / test-aarch64 (push) Successful in 6m48s
    Gates / frontend (push) Successful in 1m22s
    Gates / container (push) Successful in 9s
    CI / gates (push) Successful in 23m18s
    Gates / frontend (push) Successful in 1m8s
    Gates / package (push) Successful in 40s
    Release / guard (push) Successful in 17s
    Gates / test (push) Successful in 1m35s
    Gates / test-aarch64 (push) Successful in 6m55s
    Gates / container (push) Successful in 9s
    Release / gates (push) Successful in 9m23s
    Release / publish (push) Successful in 4m26s
    Stable

    mokhtar released this 2026-08-20 21:24:12 +00:00 | 67 commits to master since this release

    Operational failures get a page of their own, and the query log stops wearing out the disk it lives on: the deployed Pi was writing half a gigabyte a day to store two megabytes of query rows, one transaction per query. Both came out of running 0.0.6 on real hardware.

    Added

    • A diagnostics page. Operational failures now land in one curated log instead of only journald: blocklist download failures, certificate reload failures, disk pressure, query-log writer and maintenance failures, upstream exchange and history failures, client tracking failures, listener and configuration problems at boot, and the query-log recreation an upgrade causes. One entry per failing subject — an entry opens on the first failure, counts repeats, and closes itself when the subject recovers; nothing needs dismissing. Each entry says what it means for the service and what to do about it. GET /api/diagnostics serves the log, GET /api/health reports the active counts and degrades while the diagnostics store itself cannot write, and /metrics gains nxdns_diagnostics_active_warnings, nxdns_diagnostics_active_errors and nxdns_diagnostics_write_failures_total. Resolved entries can be purged when you decide the history has served its purpose — one entry from its row or its detail page, or the whole resolved history at once with "Purge all resolved" (DELETE /api/diagnostics/{id} and DELETE /api/diagnostics). An entry that is still failing is the current state of the box, not history, so it has no purge action and the API answers 409.

    Changed

    • The query log commits once a minute instead of once a query. The writer batched for 100 milliseconds, which at a household's query rate means almost every query got a transaction of its own — and a transaction costs the disk far more than the row it carries. On the deployed Pi that came to roughly 0.5 GiB of writes a day to store 2.3 MB of query rows, the kind of write volume that kills an SD card. The batch window is now logging.query_log_flush_interval_s: 60 seconds by default (the same minute Pi-hole's DBinterval defaults to, for the same reason), anything from 0 to 3600, editable on the settings page. Batches are still capped at 100 rows, so a burst is committed as soon as it fills one rather than waiting out the window, and the in-memory queue, its drop-oldest backpressure and retention are untouched. The price is two kinds of lag: a crash costs about one interval of query history — more if the writer was held back by a full disk or a slow write — and every query-log-backed view — the query-log page, the dashboard totals, the timeseries — is about one interval behind. The live page is not affected; it is fed before the queue. Set the key to 0 for the old write-immediately behavior.

    Fixed

    • Shutdown no longer races the last query rows to the disk. The query-log writer was stopped by the same cancellation that stopped the DNS listeners, so whether the batch it was holding reached the database depended on which happened to land first, the cancellation or the queue closing. Shutdown now stops and joins the listeners and every other query producer first, then closes the queue, then waits for the writer to finish emptying it — the held batch and everything still queued get written. If free space is below the critical threshold and the disk monitor will not let that final write through, the rows are counted as dropped instead of holding the exit open indefinitely.
    • An upstream success rate no longer rounds up to 100.0% while failures stand. One decimal place cannot hold 12,696 successes out of 12,698 attempts: it rounded to 100.0%, so the row claimed perfect reliability next to a failure count of 2. Neither end of the scale is reachable by rounding any more — 100.0% needs an actual absence of failures and 0.0% an actual absence of successes, and a rate a hair off either end shows 99.9% or 0.1% instead.
    • A query log set aside by a schema change is no longer named corrupt. Every recreate wrote the old file to querylog.db.corrupt-<unix seconds>, whatever sent it there — including the fingerprint mismatch an upgrade causes, where the file is a healthy database this build simply cannot read. The name is the only account of the reason that outlives the log line, so it read as an accusation and invited operators to delete an intact file. The name now says which of the four cases it hit: querylog.db.corrupt-…, .not-a-database-…, .quick-check-failed-… or .schema-changed-…. The 0.0.6 upgrade produces schema-changed. Nothing else about the recreate changed, and no existing aside file is renamed.

    Artifacts

    0df1353252a817e929438b5e6c1bf8c58be7862c0b46dc7ef167752177f6a2c0  nxdns-0.0.7-x86_64-linux-musl.tar.gz
    368d51d30ea2ec3721786c7ea973440827b1dadc54e16d01612e36670dfec3db  nxdns-0.0.7-aarch64-linux-musl.tar.gz
    7b2527ab6fcc0d16fe7cb9d5168961058e7fc4656f262d3adc17506f862166dc  IMAGE-DIGEST.txt
    
    git.mial.net/mokhtar/nxdns:0.0.7@sha256:28363f6a9a7add846cb21d5739c037d20be3df396d4c9b6a3e9f28f4c87e9ec3
    

    Compare v0.0.6...v0.0.7

    Commits since v0.0.6
    324704b build: bump version to 0.0.7
    addf24f query log batching: one transaction per flush interval, not per query
    037f209 milestone 27: diagnostics — operational failures land in one curated log, resolved history purgeable
    3dd8214 dashboard: a success rate never rounds up to 100.0% while failures stand
    64c0d72 querylog: the aside file's name says why, so a schema change is not called corrupt
    
    Downloads