From 23f4d3b04b59bf947477408b53ed5522a93cbf4b Mon Sep 17 00:00:00 2001 From: mananuf Date: Fri, 22 May 2026 16:11:04 +0100 Subject: [PATCH] metric: replace 10 duplicated bucket literals with leanSpec-named constants (#303) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ten of gean's histograms in node/metrics.go used inline []float64 literals whose values already matched one of the four named bucket constants leanSpec defines in subspecs/metrics/registry.py:36-45. The literals were copy-pasted across the file — same bucket boundaries duplicated 2-6 times per shape. Add 4 package-private vars (forkChoiceBlockBuckets, attestationValidationBuckets, stateTransitionBuckets, reorgDepthBuckets) at the top of the Histograms block, with a cross-reference comment to the spec line numbers. Replace the 10 matching Buckets: lines with references to the named vars. Zero behavior change: each replaced literal had the same values as the named constant it now points to. Verified by reading the spec file side-by-side and by go test ./node/... passing. Spec-compliance: addresses the standing ⚠️ flag from /spec-compliant devnet3 head ("Histogram bucket boundaries should be cross-checked vs spec constants"). Bucket boundaries used in gean are now traceable to a spec constant via grep. 11 histograms remain hand-rolled — mostly non-time metrics (byte sizes, payload counts, coverage ratios) where the spec's four time-bucket constants don't semantically apply. Left as-is. Closes #303. --- node/metrics.go | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/node/metrics.go b/node/metrics.go index 62f1592..29224d5 100644 --- a/node/metrics.go +++ b/node/metrics.go @@ -127,29 +127,39 @@ var ( // --- Histograms --- +// Bucket sets mirror leanSpec's named constants in +// lean_spec/subspecs/metrics/registry.py:36-45. Defined here once so the +// histograms that share these shapes don't each carry a duplicated literal +// that can drift out of spec alignment silently. Histograms whose latency +// shape doesn't fit any of the four spec constants use their own hand-rolled +// bucket array inline and note that fact in a comment. +var ( + forkChoiceBlockBuckets = []float64{0.005, 0.01, 0.025, 0.05, 0.1, 1, 1.25, 1.5, 2, 4} + attestationValidationBuckets = []float64{0.005, 0.01, 0.025, 0.05, 0.1, 1} + stateTransitionBuckets = []float64{0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3, 4} + reorgDepthBuckets = []float64{1, 2, 3, 5, 7, 10, 20, 30, 50, 100} +) + var ( metricBlockProcessingTime = promauto.NewHistogram(prometheus.HistogramOpts{ Name: "lean_fork_choice_block_processing_time_seconds", Help: "Time to process a block", - Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 1, 1.25, 1.5, 2, 4}, + Buckets: forkChoiceBlockBuckets, }) metricAttestationValidationTime = promauto.NewHistogram(prometheus.HistogramOpts{ Name: "lean_attestation_validation_time_seconds", Help: "Time to validate attestation data", - Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 1}, + Buckets: attestationValidationBuckets, }) metricCommitteeAggregationTime = promauto.NewHistogram(prometheus.HistogramOpts{ - Name: "lean_committee_signatures_aggregation_time_seconds", - Help: "Time to aggregate committee signatures", - // Mirrors leanSpec's STATE_TRANSITION_BUCKETS (lean_spec/subspecs/ - // metrics/registry.py:42) — the closest spec-named bucket constant - // for state-transition-shaped latency. - Buckets: []float64{0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3, 4}, + Name: "lean_committee_signatures_aggregation_time_seconds", + Help: "Time to aggregate committee signatures", + Buckets: stateTransitionBuckets, }) metricPqSigSigningTime = promauto.NewHistogram(prometheus.HistogramOpts{ Name: "lean_pq_sig_attestation_signing_time_seconds", Help: "Time to sign an attestation", - Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 1}, + Buckets: attestationValidationBuckets, }) metricAttestationsProductionTime = promauto.NewHistogram(prometheus.HistogramOpts{ Name: "lean_attestations_production_time_seconds", @@ -159,7 +169,7 @@ var ( metricPqSigVerificationTime = promauto.NewHistogram(prometheus.HistogramOpts{ Name: "lean_pq_sig_attestation_verification_time_seconds", Help: "Time to verify an individual attestation signature", - Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 1}, + Buckets: attestationValidationBuckets, }) metricPqSigAggBuildingTime = promauto.NewHistogram(prometheus.HistogramOpts{ Name: "lean_pq_sig_aggregated_signatures_building_time_seconds", @@ -179,27 +189,27 @@ var ( metricForkChoiceReorgDepth = promauto.NewHistogram(prometheus.HistogramOpts{ Name: "lean_fork_choice_reorg_depth", Help: "Depth of fork choice reorgs", - Buckets: []float64{1, 2, 3, 5, 7, 10, 20, 30, 50, 100}, + Buckets: reorgDepthBuckets, }) metricSTFTime = promauto.NewHistogram(prometheus.HistogramOpts{ Name: "lean_state_transition_time_seconds", Help: "Time to process full state transition", - Buckets: []float64{0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3, 4}, + Buckets: stateTransitionBuckets, }) metricSTFSlotsTime = promauto.NewHistogram(prometheus.HistogramOpts{ Name: "lean_state_transition_slots_processing_time_seconds", Help: "Time to process slots", - Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 1}, + Buckets: attestationValidationBuckets, }) metricSTFBlockTime = promauto.NewHistogram(prometheus.HistogramOpts{ Name: "lean_state_transition_block_processing_time_seconds", Help: "Time to process block in state transition", - Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 1}, + Buckets: attestationValidationBuckets, }) metricSTFAttestationsTime = promauto.NewHistogram(prometheus.HistogramOpts{ Name: "lean_state_transition_attestations_processing_time_seconds", Help: "Time to process attestations", - Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 1}, + Buckets: attestationValidationBuckets, }) metricBlockBuildingTime = promauto.NewHistogram(prometheus.HistogramOpts{ Name: "lean_block_building_time_seconds",