feat(monitoring): opt-in per-SV1-client hashrate metric - #803
Open
gimballock wants to merge 2 commits into
Open
Conversation
The tProxy exposed only `sv1_clients_total` and `sv1_hashrate_total`, so
Prometheus could not answer "what was this miner's hashrate over the last
six hours". The per-client data was already collected on every refresh —
`refresh()` populates `sv1_clients` — and then dropped, because
`update_metrics()` consumed only the aggregates.
Emit `sv1_client_hashrate{user_identity}` from that existing data.
Opt-in via `PrometheusMetrics::with_sv1_per_client`, because the series
count scales with connected miners; `with_sv1_monitoring` takes a flag to
select it. The JSON API serves per-client state either way.
Labelled by SV1 username rather than a connection id, so a reconnecting
miner continues the same series. Connections sharing a username are summed
rather than clobbering each other. Series for departed miners are removed
on the next refresh via the existing stale-label mechanism, so a miner
disconnecting does not leak a series.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Gates the per-SV1-client Prometheus series added in the previous commit. Defaults to off, so existing deployments are unaffected and an operator opts in only where the miner population is known to be bounded. Documented in the config examples, commented out, alongside the existing monitoring settings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gimballock
marked this pull request as ready for review
August 24, 2026 22:50
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.
The tProxy exposes only
sv1_clients_totalandsv1_hashrate_total, so Prometheus cannot answer "what was this miner's hashrate over the last six hours." The per-client data is already collected on every refresh and then dropped:refresh()populatessv1_clients, butupdate_metrics()consumes only the aggregates. This emits a series from data already in hand.SV1 miners behind the translator are typically the on-premise machines an operator most needs to inspect individually.
/api/v1/sv1/clientsanswers "what is this miner doing now" well, but has no history, so that question currently has no answer on any surface.Changes
sv1_client_hashrate{user_identity}, sourced from the existingsv1_clientssnapshot.PrometheusMetrics::with_sv1_per_client, selected by a newper_client_metricsflag onwith_sv1_monitoring, driven by amonitoring_sv1_per_client_metricsconfig field on the translator that defaults to off. Existing deployments are unaffected, and the JSON API serves per-client state either way.monitoring/README.mdand, commented out, in the tproxy config examples.Design notes
Label choice.
user_identity(the SV1 username) rather thanclient_id, becauseclient_idchanges on every reconnect and would fragment a miner's history into a new series per connection. Connections sharing a username are summed rather than clobbering one another, since that is the operator-meaningful answer.Series are reaped. Departed miners are removed on the next refresh through the existing stale-label mechanism, so a disconnect does not leak a series. Covered by a test that asserts the series set is empty after all miners disconnect — this is the part most likely to regress silently.
Cardinality. Opt-in is the only bound here. A tighter mechanism (top-N by hashrate, or an allowlist) can layer on if wanted; I left it out because opt-in already prevents an unbounded default and I would rather not invent a bounding policy unilaterally. Happy to add one if you'd prefer it in this PR.
Caveat worth documenting
The per-client
hashrateis vardiff-driven, and #262 records it going stale whenenable_vardiff = falseand the upstream adjusts difficulty viaSetTarget. This metric inherits that, so it is an estimate rather than measured throughput.Verified
stratum-apps110/110 withmonitoringand 120/120 withasic-rs-telemetry(the translator's default);translator_sv248/48; integration-tests and pool still compile;clippyandfmtclean. Config knob deserialization is covered by a test asserting both the default-off and explicitly-enabled cases.🤖 Generated with Claude Code