feat(metrics): add gossip arrival timing metrics - #1199
Open
MegaRedHand wants to merge 1 commit into
Open
Conversation
Every consensus message has an interval it was due in. A block belongs at the start of its own slot, an attestation one interval later, an aggregate at the aggregation interval. Nothing measured how far off a message actually landed, so a slow network looked the same as aggregators that were slow to prove. Six metrics cover it. Three histograms carry the absolute distance from the boundary, and three counters label each arrival by position: before, inside, or after the interval it was due in. Absolute distance keeps an early arrival and a late one of the same size in a single bucket, and the position label is what tells them apart. Blocks anchor to interval 0 of their own slot, attestations to interval 1 of their data slot. Aggregates anchor instead to the most recent aggregation boundary at or before the arrival. An aggregate catching up on an earlier group carries a data slot several slots back, and anchoring there would report a multi-slot delay that is not a health problem. The boundary anchor also bounds the value to one slot and rules out `before`, so the aggregate counter exports two series rather than three. It wraps rather than clamps, so a message that beat its boundary reads as nearly a slot late instead of as early, a limitation the helper documents. Only messages that crossed the network are sampled, all three kinds. A proposer's own block and a validator's own votes come back through the same handlers with no peer id (see `publish_and_process_block` in node.py), and stamping those would sample local build latency as if it were network timing. Aggregates this node produced are left out for the same reason: their timing is proving cost, which `lean_pq_sig_aggregated_signatures_building_time_seconds` already measures directly. A message's slot is attacker-controlled until the store validates it, and `Slot` is a `Uint64`. One block claiming slot 2**64-1 would push a single observation of roughly 7e19 seconds into the histogram and pin `_sum` for the life of the process, so arrivals further than MAX_MEASURABLE_SLOT_DISTANCE from the clock are dropped rather than measured. The aggregate metric takes no slot and needs no such guard. `SlotClock._milliseconds_since_genesis` becomes public: the arrival helpers need millisecond resolution, which the slot and interval accessors have already rounded away. Names, types, and buckets match the leanMetrics registry entry.
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.
🗒️ Description
Every consensus message has an interval it was due in. A block belongs at the start of its own slot, an attestation one interval later, an aggregate at the aggregation interval. Nothing measured how far off a message actually landed, so a slow network looked the same as aggregators that were slow to prove.
Six metrics cover it. Three histograms carry the absolute distance from the boundary, and three counters label each arrival by
position:before,inside, orafterthe interval it was due in. Absolute distance keeps an early arrival and a late one of the same size in a single bucket, and the position label is what tells them apart.lean_gossip_block_arrival_delay_secondslean_gossip_attestation_arrival_delay_secondslean_gossip_aggregation_arrival_delay_secondslean_gossip_block_arrival_totalposition=before,inside,afterlean_gossip_attestation_arrival_totalposition=before,inside,afterlean_gossip_aggregation_arrival_totalposition=inside,afterAggregates anchor to the latest boundary rather than their own data slot. An aggregate catching up on an earlier group carries a data slot several slots back, and anchoring there would report a multi-slot delay that is not a health problem. The boundary anchor also bounds the value to one slot and rules out
before, so that counter exports two series rather than three. It wraps rather than clamps, so a message that beat its boundary reads as nearly a slot late instead of as early; the helper documents that limitation.Only messages that crossed the network are sampled, all three kinds. A proposer's own block and a validator's own votes come back through the same handlers with no peer id (see
publish_and_process_blockinnode.py), and stamping those would sample local build latency as if it were network timing. Aggregates this node produced are left out for the same reason: their timing is proving cost, whichlean_pq_sig_aggregated_signatures_building_time_secondsalready measures directly.A message's slot is attacker-controlled until the store validates it, and
Slotis aUint64. One block claiming slot2**64-1would push a single observation of roughly 7e19 seconds into the histogram and pin_sumfor the life of the process, so arrivals further thanMAX_MEASURABLE_SLOT_DISTANCEfrom the clock are dropped rather than measured. The aggregate metric takes no slot and needs no such guard.SlotClock._milliseconds_since_genesisbecomes public: the arrival helpers need millisecond resolution, which the slot and interval accessors have already rounded away.Names, types, and buckets match the leanMetrics registry entry, which adds these six rows and a "Gossip arrival timing" dashboard row. EthLambda implements all six today.
Validation
Checked against a live 2-node devnet (test XMSS scheme, 8 validators split 4/4, real QUIC gossip) rather than unit tests, matching how the rest of
node/metricsis covered. For every family on both nodes, the counter totals matched the histogram counts exactly, and each delta recomputed independently from the log timestamps landed inside the window that second-resolution logs allow.The anchor difference is visible in the data: aggregation delay stayed bounded under one slot (mean ~1.7–2.1s) while block and attestation delays ran to several seconds, since this client lags and those two anchor to their own slot.
beforenever fired on any family.One thing the run turned up, out of scope here and left alone:
cli/run.pysubscribes to the block and attestation-subnet topics but never to the aggregation topic, so a node publishes aggregates that no peer receives (Empty mesh ... subscribed=0every heartbeat). Until that is fixed,lean_gossip_aggregation_*stays at zero on a real node. I subscribed locally to exercise the metric; that patch is not part of this PR. Happy to open a separate one if it is not already known.🔗 Related Issues or PRs
N/A
✅ Checklist
just check