refactor(runtime)!: rename the dispatch latency and source reconnect metrics - #279
Merged
Conversation
mfw78
force-pushed
the
refactor/sources-not-subscriptions
branch
from
August 18, 2026 01:14
b172b21 to
b5c4bba
Compare
mfw78
force-pushed
the
refactor/source-metrics
branch
from
August 18, 2026 01:16
722f1b0 to
77160fd
Compare
…metrics Rename nexum_runtime_event_latency_seconds to nexum_runtime_dispatch_latency_seconds and nexum_runtime_stream_reconnects_total to nexum_runtime_source_reconnects_total, and rename the reconnect counter label kind to source_kind. The label values stay block and chain-log: the value names the RPC transport, not a trigger. The chain-log reconnect task's tracing field carried event; it now carries chain-log, so the log line and the series agree for one source. The dropped and latency help strings now say triggers, and the reconnect help describes the emitted label set. docs/production.md section 6 and the section 7 alert rules move to the new names in the same change. The exporter now sets buckets for the latency histogram. Without them metrics-exporter-prometheus renders a histogram as a quantile summary, so the section 7 latency rule read a _bucket series that never existed and could never fire, and the section 6 type column was untrue. A test renders the recorder and holds the metric to its bucket series. BREAKING CHANGE: a Prometheus series is its full label set including __name__, so each renamed series ends at its cumulative value and a new series starts at zero across the deploy; rate() and increase() lose the delta at that boundary. There is no dual-emit window. Closes #243 AI Assistance: Claude Fable 5 used for the implementation.
mfw78
force-pushed
the
refactor/source-metrics
branch
from
August 18, 2026 01:18
77160fd to
4c48d04
Compare
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.
This is the last item in the vocabulary freeze.
nexum_runtime_event_latency_secondsandnexum_runtime_stream_reconnects_totalwere the two metric names still speaking the retired vocabulary, and the reconnect counter'skindlabel was the last barekindon a metric surface.Operator impact
A Prometheus series is its full label set including
__name__, so a rename is not a rename to Prometheus. It is a death and a birth. At the deploy that carries this change, both old series stop being scraped and end at whatever cumulative value they last held, and the new series begin at zero.rate()andincrease()lose the delta across that boundary: the last window before the deploy and the first window after it both see a truncated series, and the reconnect counter's apparent reset is not a process restart, it is the old name going away.nexum_runtime_event_latency_secondsnexum_runtime_dispatch_latency_secondsnexum_runtime_stream_reconnects_totalnexum_runtime_source_reconnects_totalnexum_runtime_stream_reconnects_total{kind="..."}nexum_runtime_source_reconnects_total{source_kind="..."}The label values are unchanged.
blockandchain-logare still the two values the reconnect counter carries,blockper chain andchain-logalso carryingmodule. Nothing else in the metric set moves: the other seven names, their types and their label sets are exactly as they were.There is deliberately no dual-emit window
The usual courtesy for a metric rename is to emit both names for a release so dashboards and rules can be moved without a gap. This change does not do that, on purpose. Nothing is published, there is no released binary carrying the old names into anyone's Prometheus, and no dashboard or rule set exists anywhere outside
docs/production.md, which moves to the new names in this same commit. A migration window exists to give somebody time to migrate. There is nobody to migrate. Building the dual emit would mean adding a second emit site at each of the three call sites, adding the shadow names to theMETRICStable so the emitted-name scan stays green, documenting the deprecation in the handbook, and then reverting all of it a release later. That is machinery built to be removed, and it would put the retired vocabulary back intometrics.rs, which is the one file this freeze exists to clean.The kind label
Renaming the label to
source_kindforced the question of what its values should say, because the chain-log task disagreed with itself: the metric emitted"chain-log"while the tracing side emittedNextTrigger::StreamPanic("event"), so one source produced two words on two surfaces an operator correlates by hand.The value stays
chain-logon both, and the tracing side moved to match. The reasoning is that this value names the RPC transport, aneth_getLogslog stream, and the transport layer deliberately spellslogbecause that is what the RPC returns.eventis precisely the trigger word this freeze retires, so promoting it to a transport value would be the same category error running the other way. The issue's "does not change" section fixes the value set atblockandchain-log, and that is what both surfaces now carry.The tracing field keeps the name
kindrather than following the metric label tosource_kind. The divergence the issue describes is a disagreement of values, not of field names, andevent_loop.rsis a contested file where only what the issue names should move.The handbook's alert rules
Section 7's six rules were all checked against the emitted name set, not just the two that mention the renamed series.
NexumReconnectStormandNexumDispatchLatencywere rewritten onto the new names in this commit.NexumModulePoisoned,NexumModuleTraps,NexumRpcErrorRateandNexumDownreferencenexum_runtime_module_poisoned,nexum_runtime_module_errors_total{error_kind},nexum_runtime_chain_request_total{outcome}andup{job="nexum"}, all of which still exist and match the scrape config in section 6. No rule in the handbook names a series that stopped existing at this deploy.Section 6's metric table moves in the same commit, so the
METRICStable, the emitted names and the handbook agree, and the emitted-name scan holds in both directions.Review findings
Eight findings were raised across the review passes, four distinct after deduplication. Two were fixed, two were rejected.
Fixed, blocker:
NexumDispatchLatencyread a_bucketseries the exporter never emitted. The rule readsrate(nexum_runtime_dispatch_latency_seconds_bucket[10m])grouped byle, butPrometheusAddOnbuilt a barePrometheusBuilder::new()with no bucket configuration on either install path, andmetrics-exporter-prometheusrenders a histogram with no configured buckets as a quantile summary. Rendering a live recorder confirmed it:# TYPE ... summary,quantilelabelled series,_sumand_count, no_bucketand noleanywhere. The rule matched an empty vector at every evaluation, so it could never enter pending and could never fire. It was equally dead before the rename, under the old name, and the rename would have carried a silent alert forward wearing a fresh name. Fixed by setting explicit buckets fornexum_runtime_dispatch_latency_secondson both install paths, with bounds spanning the rule's 5 s threshold, and by a test that renders a local recorder and asserts the exposition is# TYPE ... histogramwith_bucketseries andlelabels, so the rule and the exporter cannot drift apart again.Fixed, major: section 6 declared the latency metric's type as
histogramwhile the exposition saidsummary. The Type column is the operator's contract for how to aggregate a series, and it was untrue, which is what leads a reader to write a_bucketquery and get an empty panel with no error. The buckets fix resolves this at the source rather than by editing the cell: the exporter now emits a real Prometheus histogram, so the row's declared type and label set are accurate as written, and the new test pins them.Rejected, minor: the reopen path logs "event source reopened" immediately above a counter that emits
source_kind="chain-log". The observation is correct and the mismatch is real, but the fix was not taken. The issue's done-when requires the tracing field and the metric label to agree, and they do, bothchain-log. The issue also explicitly endorses the "event source" prose vocabulary elsewhere in its text, so changing it here would contradict the specification this change implements, andevent_loop.rsis a contested file where only what the issue names may move. The prose sweep belongs to its own change with its own specification.Rejected, minor: a stray untracked test file was reported to fail
just ci.crates/nexum-runtime/tests/zz_reviewer_probe.rsdoes not exist. Thetests/directory is absent from the crate, the worktree is clean, and the full run passes 785 of 785.Verification
nix develop --command just ciis green on this commit: fmt, clippy under-D warnings, nextest at 785 passed of 785, doctests and content-lint. That count includes the new bucket rendering test.Closes #243
AI Assistance: Claude Fable 5 and Claude Opus 5 used for the implementation, review, and this description.