Add hard-failure health checks with a dashboard banner - #35
Merged
Conversation
Every problem this pool has had was found by someone deciding to run a query: duplicate shares, a fake fee, an audit that compared two numbers the proxy wrote, templates carrying no sidechain commitments, and most recently accepted shares destroyed by a locked DB. None of them announced themselves. Seven checks, each one binary and actionable, surfaced as a banner on every page and as GET /health (503 when failing, so an uptime checker can watch it): events_lost accepted work that never reached the DB duplicate_shares the extranonce1 collision class ledger credit vs rate_used, rate vs template, orphan rates margin owed more than mined payout_ambiguous in-flight rows with no txid — needs a human payout_stalled a batch unconfirmed for over an hour template_commitments blocks no sidechain can merge-mine into Deliberately excluded: BMM capture, settlement latency, backlog size, reserve headroom. They are worth watching but they fluctuate, and a banner that is sometimes red for a slow afternoon is a banner nobody reads. events_lost needed a home in the DB. It was process-local, readable only at shutdown, so no query could see it — pool_meta now mirrors it, written on the template path, which is a different connection state from the batch commit that failed. Evaluated on a timer, not per request: the duplicate-hash scan measured 2.0s against 337k shares and better-sqlite3 is synchronous, so running it on the request path would stall the dashboard on every 15s auto-refresh. The timer keeps full-history coverage — a windowed check would go green on a duplicate from two days ago that nobody noticed. A check that cannot run reports unavailable, never a pass, so an old DB does not read as a healthy one. No snapshot yet reports "checking", for the same reason.
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.
Why
Every problem this pool has had was found by someone deciding to run a query — duplicate shares, a fee that never reached the accrual path, an audit that compared two numbers the proxy itself wrote, templates carrying no sidechain commitments, and most recently accepted shares destroyed by a momentarily locked DB. None of them announced themselves.
What
Seven checks, each binary and actionable, on every page as a banner and at
GET /health(503 when failing, so an uptime checker can watch it):events_lostduplicate_sharesledgermarginpayout_ambiguouspayout_stalledtemplate_commitmentsDeliberately excluded: BMM capture, settlement latency, backlog size, reserve headroom. Worth watching, but they fluctuate — a banner that goes red for a slow afternoon is a banner nobody reads. A payout settling normally has an explicit test asserting it stays quiet.
Two things that shaped the design
events_losthad nowhere to live. It was process-local and readable only at shutdown, so no query could see it.pool_metanow mirrors it, written on the template path — a different connection state from the batch commit that failed.The duplicate check is slow. Measured against the live DB:
better-sqlite3 is synchronous, so running that per request with a 15s auto-refresh would stall the dashboard. It runs on a 5-minute timer and pages read the snapshot. A windowed check would have been cheap, but it would go green on a duplicate from two days ago that nobody noticed — the timer keeps full-history coverage.
Not-a-pass semantics
A check that cannot run reports
unavailable, neverok, so a DB predating a table doesn't read as healthy. Before the first pass,/healthreturns 503checkingrather than a green light.Tests
18 new tests. Each failure mode gets a DB built in that state; healthy and normal-operation cases assert silence. Includes a regression test for a bug the check would otherwise have had —
COUNT(*)counts NULL hashes thatCOUNT(DISTINCT ...)ignores, which would report every legacy row as a duplicate.64/64 dashboard tests pass;
makeclean under-Werror; C suite passes.