meta: expose the background subsystems on /metrics - #78
Open
bjmeetsfo wants to merge 1 commit into
Open
Conversation
Conviction, shard-divergence reconciliation, retention, freeze aging and rebalancing all run on background loops and reported what they did only through tracing. That leaves an operator scraping logs to answer the questions they actually ask during an incident: is the fleet being convicted right now, is any location in safe mode, how many shards is the metaserver routing to nodes that do not serve them, is retention keeping up. meta/subsystem_metrics.rs adds the recorder those loops write their round outcomes into, rendered onto the metaserver's existing /metrics surface alongside the request and inventory series already there. It holds two kinds of series, and the distinction is the point. Counters only climb: resources convicted, convictions held back by a guard, reboots detected, divergences found, shards reassigned by cause, tombstones purged, frozen resources aged out, rounds completed per subsystem. These answer "is this happening, and how often". Gauges describe the most recent round: per-location damage severity and abnormal count, whether a location is in safe mode, whether a detector is paused, how many shards are diverged or settling right now, what the retention cap held back. These answer "what is true now", and are replaced wholesale each round - so a location that recovers reports zero rather than sticking at its worst value, and a location that disappears stops reporting at all. Both are covered by tests, because a gauge that latches is a gauge that pages after the incident is over. The recorder lives on SingleNodeMeta and is shared by clone, so every handle writes to the same series and the loops need no extra plumbing. Recording happens at each subsystem's entry point rather than in the loop bodies, which means a direct call - including from a test or an admin route - is counted too. The raft backend renders nothing, because it drives none of these loops. reassign_shard grows a reason-carrying variant so a move can be attributed: ShardReassignmentReason gains as_str, matching its serde representation so a dashboard and a JSON payload name the same cause the same way. The existing reassign_shard keeps working and records "unspecified". A poisoned metrics lock recovers rather than propagating: observability is not worth taking the metaserver down for. Label values are escaped, since locations and reasons are operator supplied and a stray quote would otherwise corrupt the exposition. One cardinality note in the module docs: damage is labelled by location, which matches the reference's per-tag emission and is bounded by the number of distinct locations - but with hierarchical locations a deployment labelling every rack separately gets one series per rack. 11 new tests: counters accumulating across rounds, damage gauges describing only the latest round, a location that disappears no longer reporting, the two tiers reporting separately, divergence keeping a running total beside a current state, retention and aging counters climbing by kind, reassignments labelled by cause, label escaping, every series carrying HELP and TYPE, a clone sharing one recorder, and the /metrics route exposing the new series end to end.
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 problem
Conviction, shard-divergence reconciliation, retention, freeze aging and rebalancing all run on background loops and reported what they did only through tracing.
That leaves an operator scraping logs to answer the questions they actually ask during an incident:
The reference metaserver emits per-tag counters for exactly these (
server_convict_count,server_damage_severity,abnormal_server_count,missing_partition_count,balance_partition_count,freeze_partition_count).What this adds
meta/subsystem_metrics.rs— the recorder those loops write their round outcomes into, rendered onto the metaserver's existing/metricssurface alongside the request and inventory series already there.It holds two kinds of series, and the distinction is the point:
Counters only climb — resources convicted, convictions held back by a guard, reboots detected, divergences found, shards reassigned by cause, tombstones purged, frozen resources aged out, rounds completed per subsystem. These answer "is this happening, and how often".
Gauges describe the most recent round — per-location damage severity and abnormal count, whether a location is in safe mode, whether a detector is paused, how many shards are diverged or settling, what the retention cap held back. These answer "what is true now", and are replaced wholesale each round: a location that recovers reports zero rather than sticking at its worst value, and a location that disappears stops reporting at all. Both are covered by tests, because a gauge that latches is a gauge that pages after the incident is over.
Design notes
SingleNodeMetaand is shared by clone, so every handle writes to the same series and the loops need no extra plumbing.reassign_shardgrows a reason-carrying variant so a move can be attributed.ShardReassignmentReasongainsas_str, matching its serde representation so a dashboard and a JSON payload name the same cause the same way. The existingreassign_shardkeeps working and recordsunspecified.Series
temporalstore_meta_convicted_totaltiertemporalstore_meta_conviction_held_totaltier,guardtemporalstore_meta_detector_rounds_totalsubsystemtemporalstore_meta_reboots_detected_totaltemporalstore_meta_damage_severitytier,locationtemporalstore_meta_abnormal_resourcestier,locationtemporalstore_meta_location_safe_modetier,locationtemporalstore_meta_detector_pausedtiertemporalstore_meta_shard_divergence_totaltemporalstore_meta_shard_divergencestatetemporalstore_meta_shards_reassigned_totalreasontemporalstore_meta_retention_purged_totalkindtemporalstore_meta_retention_blockedtemporalstore_meta_retention_cappedtemporalstore_meta_freeze_aged_totalkindCardinality note (also in the module docs): damage is labelled by location, matching the reference's per-tag emission and bounded by the number of distinct locations — but with hierarchical locations (#73) a deployment labelling every rack separately gets one series per rack.
Tests
11 new tests: counters accumulating across rounds, damage gauges describing only the latest round, a location that disappears no longer reporting, the two tiers reporting separately, divergence keeping a running total beside a current state, retention and aging counters climbing by kind, reassignments labelled by cause, label escaping, every series carrying
HELPandTYPE, a clone sharing one recorder, and the/metricsroute exposing the new series end to end.Verification:
cargo test -p temporalstore-rust --lib meta::subsystem_metrics— 10 passed, 0 failed.cargo test -p temporalstore-rust --lib meta— 198 passed, 0 failed.cargo test -p temporalstore-rust --bin metaserver— 18 passed, 0 failed.cargo build -p temporalstore-rust --bin metaserver— clean, no new warnings.No gate: this only adds series to an endpoint that already exists, and changes no behaviour. Covers the subsystems merged so far; the guards in #75 and #76 can add their own series when those land.