Eagerly register invalid counter metrics at startup#245
Conversation
… with value 0 Counters like lean_attestations_invalid_total, lean_pq_sig_aggregated_signatures_invalid_total, and lean_pq_sig_attestation_signatures_invalid_total may never be incremented during normal operation. Because they used function-scoped LazyLock statics, they were only registered with Prometheus on first increment — meaning they were absent from /metrics output entirely if no invalid event occurred. This moves them to module scope and forces initialization at startup via metrics::init().
🤖 Kimi Code ReviewThe PR is a clean refactoring that improves observability by ensuring "never-incremented" counters appear in Prometheus. No critical issues found. Minor suggestions:
Correctness verification:
Security/Consensus impact: None. This is purely instrumentation code with no effect on attestation validation, fork choice, or state transition logic. Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt |
🤖 Claude Code ReviewThe change is small and focused. Here's the review: PR Review: Eagerly register invalid counter metrics at startupOverall: The change is correct and addresses a real observability gap. The approach is consistent with the existing codebase patterns. CorrectnessThe mechanics are sound. One concern:
|
🤖 Codex Code ReviewNo blocking findings. The patch is narrowly scoped to Prometheus registration and does not change fork choice, attestation validation, justification/finalization, state transition, XMSS handling, or SSZ logic. Eagerly forcing these three One non-blocking maintainability note: the new I couldn’t run Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
Greptile SummaryThis PR fixes a Prometheus observability gap by eagerly registering three "invalid" counter metrics (
Confidence Score: 5/5
|
| Filename | Overview |
|---|---|
| crates/blockchain/src/metrics.rs | Promotes three "invalid" counter statics from function-scope to module-scope and adds init() to eagerly force their initialization, ensuring Prometheus sees them at value 0 from startup. No logic changes to the inc_* functions. |
| bin/ethlambda/src/main.rs | Adds a call to ethlambda_blockchain::metrics::init() before the metrics server is spawned, ensuring the three counters are registered at node startup well before any Prometheus scrape can occur. |
Sequence Diagram
sequenceDiagram
participant Main as main()
participant Metrics as ethlambda_blockchain::metrics
participant Prom as Prometheus Registry
participant Server as Metrics HTTP Server
Main->>Metrics: init()
Metrics->>Prom: LazyLock::force(&LEAN_ATTESTATIONS_INVALID_TOTAL)<br/>→ register_int_counter! (value = 0)
Metrics->>Prom: LazyLock::force(&LEAN_PQ_SIG_AGGREGATED_SIGNATURES_INVALID_TOTAL)<br/>→ register_int_counter! (value = 0)
Metrics->>Prom: LazyLock::force(&LEAN_PQ_SIG_ATTESTATION_SIGNATURES_INVALID_TOTAL)<br/>→ register_int_counter! (value = 0)
Main->>Metrics: set_node_info() / set_node_start_time() / set_attestation_committee_count()
Main->>Server: tokio::spawn(start_metrics_server)
Note over Server,Prom: All three counters already visible at /metrics with value 0
Server->>Prom: GET /metrics scrape
Prom-->>Server: lean_attestations_invalid_total 0<br/>lean_pq_sig_aggregated_signatures_invalid_total 0<br/>lean_pq_sig_attestation_signatures_invalid_total 0
Last reviewed commit: "Eagerly register inv..."
…nit() All LazyLock statics are now at module scope instead of function scope, and init() forces every metric to register with Prometheus at startup. This ensures all metrics appear in /metrics output from the first scrape, even counters that may never be incremented (e.g. invalid attestation counters). Addresses review feedback to initialize all metrics, not just the invalid ones.
Resolve conflict in metrics.rs: keep module-scoped LEAN_TABLE_BYTES from #245 (eagerly register metrics), remove redundant function-scoped static.
Motivation
Metrics using function-scoped
LazyLockstatics were only registered with Prometheus on first use. Counters that may never be incremented (e.g. invalid attestation counters) were entirely absent from/metricsoutput, causing:0rate(lean_attestations_invalid_total[5m]) > 0silently fail when the metric doesn't existFlagged by the leanMetrics team during devnet-3 metrics review.
Description
Moves all blockchain metrics from function-scoped
LazyLockstatics to module-scoped statics, and adds ametrics::init()function that eagerly registers every metric at startup viaLazyLock::force().Changes
crates/blockchain/src/metrics.rs: All 28 metric statics (14 gauges, 10 counters, 8 histograms) are now at module scope, organized by type (Gauges / Counters / Histograms). Theinit()function forces all of them. Public API functions are now thin wrappers that reference the module-scope statics directly.bin/ethlambda/src/main.rs: Callsmetrics::init()at startup before other metric initialization.Why all metrics, not just the invalid counters?
Per review feedback from @MegaRedHand — initializing all metrics makes the pattern consistent and ensures the full metric set is visible from the first Prometheus scrape. This also aligns with the module-scope style already used in
state_transition/src/metrics.rsandnet/p2p/src/metrics.rs.How to Test
cargo runcurl localhost:5054/metrics