Skip to main content

Where the slot goes: six instruments, calibrated

· 26 min read
Stefan Kobrc
Founder RockLogic
StereumLabs AI
Artificial Intelligence

Over three days in August, Lodestar timed 21,530 newPayload calls to its Ethrex peer and reported a mean of 372.6 ms; Ethrex timed the same calls from its own end of the same socket and reported 164.8 ms. Both figures are right as far as the client reporting them is concerned, since the two stopwatches start and stop at different points, but the 208 ms between them is larger than most of the differences this series has spent six editions measuring between execution clients.

For six editions we compared execution clients through whichever consensus client happened to be watching them, and it took until the sixth to work out how much of what we were reading belonged to the watcher rather than the watched. Six consensus clients run against the same fleet here, no two of them measure a slot the same way, and one of them cannot produce a mean for the engine call at all.

Where the slot goes: Lodestar, and the 200 ms between two stopwatches

· 36 min read
Stefan Kobrc
Founder RockLogic
StereumLabs AI
Artificial Intelligence

Lodestar publishes 1,175 metric names and 162 histograms on our fleet, 62% more metric names than any other consensus client we run and twenty-seven times Teku's histogram count. Part five ended by asking whether the richest instrument answers the question in this series' title better than the sparsest one did.

It does, and by a wider margin than we expected. Lodestar decomposes a block's journey into timers that add up: arrival, the wait on the execution layer, import, head. The pieces sum to the whole within 4 milliseconds. One bucket boundary sits at 4.000 seconds, so the share of blocks not processed by the attestation deadline is a count rather than an interpolation. It is 5.1% of blocks on the best execution client here and 9.0% on the worst, while only 0.11 to 0.16% of blocks arrive after 4 seconds at all.

Then the same instrument turned around and told us something about itself. Five execution clients time their own engine-API handlers, and four of them publish a mean we can subtract. Lodestar's wall clock around newPayload runs 181 to 208 milliseconds longer than what the execution client on the other end says it spent. The four other consensus clients that publish a mean sit 6 to 50 milliseconds above their peer. That gap is not a client comparison, it is a measurement about Lodestar, and it took a second stopwatch at the execution client's end to find it.

Where the slot goes: Teku, and the counter that called a syncing node healthy

· 20 min read
Stefan Kobrc
Founder RockLogic
StereumLabs AI
Artificial Intelligence

Teku is the only client in this series that answers the question in the title directly. It splits a block's journey into eleven named stages and counts every one of them, so instead of inferring where the slot goes you can read it off: arrival, gossip validation, pre-state retrieval, the engine call, the state transition, the commit.

In the same three days, on the same six machines, Teku's own counters reported that the healthiest node in the fleet was one that had not committed a single block to its canonical chain.

Both of those are worth your attention, and they are the same story. Teku gives you more slot detail than any other client here, and the detail is carried in metric types that will mislead you at least three separate ways if you read them the way their names suggest.

Where the slot goes: Grandine, and the metrics a name search cannot find

· 22 min read
Stefan Kobrc
Founder RockLogic
StereumLabs AI
Artificial Intelligence

This edition was nearly published with its central claim inverted. We ran three Prometheus metric-name searches against Grandine, across eleven substrings including payload, forkchoice, engine, execution, latency, delay and duration. The first returned nothing, the second two histograms and the third eleven, and not one of them was the engine-call timing we were looking for. The obvious conclusion was that Grandine is the sparse end of this series: a client that times block arrival and blob fetches and nothing else. A fact-check caught it before publication.

Grandine times both engine calls, per method, at more than one observation per slot. The metric is called ETH1_API_REQUEST_TIMES. It is in capitals, it contains none of the eleven substrings we searched for, and the engine method lives in a label rather than the name, so method="engine_newPayloadV4" sits inside a series whose name says only that some eth1 API was called. No name search reaches it.

Grandine is not the sparse end of this series, and it is not the rich end either: an inventory of its scrape job returns 70 histograms against Lodestar's 158 and Lighthouse's 143, and on total metric names it is the sparsest of the six at 330. The point is not where it ranks. The point is that a comparatively modest client still hid a per-method engine timer from three searches, which means the search was the problem, not the client.

What the metric shows once you find it: Besu's head-update call costs 162 ms against 4 to 7 ms for every other execution client, which is the fourth independent instrument to say so, and the middle of the execution-client field reorders again.

Where the slot goes: Prysm, and what survives three instruments

· 20 min read
Stefan Kobrc
Founder RockLogic
StereumLabs AI
Artificial Intelligence

The first two editions of this series each had one instrument. Under Lighthouse we used its block-delay gauges and found the execution client moved the would-fail-attestation rate up to 3.7x. Under Nimbus we used its arrival histogram and found it could not separate execution clients at all. Prysm gives us fine-grained histograms of both engine calls, one entry per slot, which is what we came for. It also gave us something we did not plan on: enough overlap with the other two clients to put the same question to three independent instruments on three separate pairings, in the same three days, on identical execution-client versions.

That triangulation is the finding, and it changes how we read our own earlier editions. Two results survive all three instruments: Ethrex is the fastest execution client on newPayload and Erigon the slowest, and Besu spends around 200 ms on forkchoiceUpdated while it is a single-digit or low-double-digit call for the rest. Almost nothing else does. The middle of the field, Geth against Nethermind against Besu, permutes depending on which client you ask. The reason is not the metric definition, and it is not the hardware either. At a fixed version, the same execution client's own internal timer varies by 160 ms depending on which consensus client is driving it, on machines whose load and database size are indistinguishable. That is larger than the margins we would be ranking on.

Where the slot goes: Nimbus and the execution timing it can't see

· 15 min read
Stefan Kobrc
Founder RockLogic
StereumLabs AI
Artificial Intelligence

In the first edition of this series we measured, under Lighthouse, where a 12-second slot goes against the 4-second attestation deadline, and found that the execution client you pick is the lever: it shifts how often a node lands late enough to fail attestations by about 1.5x across the mainstream clients, and 3.7x once Erigon's disk-bound tail is counted. This edition runs the same six execution clients on the same bare-metal fleet, and asks the same question of Nimbus. The answer is the finding: Nimbus cannot tell you which execution client is costing you, because the one timing it reports is block arrival, and arrival is the part the execution client does not touch.

That is not a gap in our data. It is what Nimbus exposes. Where Lighthouse breaks the path to attestable into arrival, consensus verification and execution verification, Nimbus publishes a single histogram of block-arrival delay. The good news is that this histogram is complete, counting every block, not the once-a-minute sample Lighthouse's gauges gave us. The catch is that it sees only the network-and-proposer part of the slot, so the 3.7x spread that mattered under Lighthouse is simply not in the data Nimbus reports.

Where the slot goes: Lighthouse and attestation timing

· 14 min read
Stefan Kobrc
Founder RockLogic
StereumLabs AI
Artificial Intelligence

An Ethereum slot is 12 seconds, and your attestation is due 4 seconds in. By the time your execution client sees a block to verify, most of that 4-second budget is already gone: on our fleet the block arrives, on average, 1.7 to 1.9 seconds into the slot, and that number barely moves whichever execution client you run. What the execution client changes is the slice after arrival. Under Lighthouse, that slice runs from about 100 ms (Ethrex) to 460 ms (Erigon) on a normal block. Across the mainstream clients it shifts how often the node lands late enough that attestations would fail by about 1.5x, and by 3.7x once Erigon's disk-bound tail is in the picture, on identical hardware.

This is the first of a series. We run all six execution clients paired with all six consensus clients on identical bare metal, and each consensus client reports slot timing differently. We start with Lighthouse because it instruments the block-delay breakdown more completely than any other CC on the fleet. Later editions take the same question to Prysm, Teku, Nimbus, Lodestar and Grandine.

Ethereum execution client hardware footprint, measured

· 14 min read
Stefan Kobrc
Founder RockLogic
StereumLabs AI
Artificial Intelligence

How much machine does an Ethereum execution client need once it sits at the tip of mainnet? Our fleet runs every major execution client on its own host, all with 12 cores, five of six client sets with 16 GiB RAM, one consensus client per pairing on a separate machine. Same chain, same engine-API traffic, one telemetry pipeline. Averaged over the same 36 hours, the synced clients hold between 4.9 and 15.6 GiB of host memory, burn about half a CPU core, and write to disk at anywhere from 0.9 to 22 MB/s. The spread is the story: the client you pick decides whether your box idles or works.

Tracing a Besu memory leak to a one-line method

· 9 min read
Stefan Kobrc
Founder RockLogic
StereumLabs AI
Artificial Intelligence

Six Besu nodes, same version, same hardware, same config. Five held a flat JVM heap around 1.0 to 1.3 GB. The sixth climbed about 10 GB a day and was on track to be OOM-killed roughly 30 hours after a restart. The one thing different about it was the consensus client on the other side of the engine API.

This is a walkthrough of how StereumLabs AI, reading our fleet's metrics and logs, took that one anomalous node, traced it to a single method, and filed it upstream. Besu shipped a round of mitigations and closed the issue. A later devnet reproduction showed the underlying layers still pile up, the issue was reopened, and the fix that followed is now in review. The bug is operational: recoverable by a restart, no consensus impact, no double-sign, no state-root divergence. It is also the kind of cross-client interaction a single-node test will never surface, because it only appears when a live pairing lands in a specific state.

Six identical Besu nodes over time: five hold a flat JVM heap near 1 GB while the Prysm-paired node climbs about 10 GB per day toward an out-of-memory kill

Ethereum EC pruning: disk size and the 4-second deadline

· 30 min read
Stefan Kobrc
Founder RockLogic
StereumLabs AI
Artificial Intelligence

A Geth node and an Erigon node sit in the same rack, follow the same Ethereum mainnet, take the same engine-API traffic from their paired consensus clients. Today the Geth host's root filesystem reports ~1,688 GB used. The Erigon host reports ~509 GB. That is a 3.3× spread, on the same chain, with no archive mode involved. Both are full nodes, both synced to mainnet head.

Disk is only half of it. The same pruning that bounds those footprints also runs on the engine-API hot path, and on some consensus-client pairings it pushes engine_newPayload past the 4-second sync-committee deadline. This post covers both halves: how big each client gets and whether you can see it pruning, then what that pruning costs in latency once a node is at the tip.

Read this first
  • Absolute disk numbers are upper bounds, not steady state. Every EC host here was rebuilt in the last 7 to 16 days (we checked boot times and on-chain history). A fresh node has just finished an initial sync, whose on-disk layout is looser than a long-running, compacted one. The relative ordering (Erigon < Ethrex < Nethermind ≈ Besu < Reth < Geth) comes from architecture, not host age; read "Geth ~1.7 TB" as "heaviest by a wide margin," not a capacity-planning figure.
  • We run no validators on this fleet. The latency half measures engine_newPayload, the EC's slice of a validator's slot budget, not actual missed duties. Read those figures as a risk indicator, not a miss count.
  • newPayload P99 is observer-dependent. Each CC calls notifyNewPayload at a different point in the slot, so the same EC reads differently per CC. Nimbus is the headline; the full matrix is in the latency section.
  • Reth never finished syncing on this fleet, so it is excluded from the synced comparisons throughout (details in its section).

Datadir sizes per Ethereum execution client compared on identical hardware, ranging from Erigon ~509 GB to Geth ~1,688 GB

Key findings:

  • 3.3× footprint spread on the same chain (root-FS used per host): Erigon ~509 GB, Ethrex ~598 GB, Nethermind ~1,225 GB, Besu ~1,261 GB, Reth ~1,352 GB (not synced), Geth ~1,688 GB.
  • Five of six ECs grew 15 to 19 GB in the 7-day window (~2–3 GB/day, normal block ingestion). The cumulative spread comes from retention policy, not the past week's growth.
  • Each EC exposes a different pruning surface, and Ethrex exposes none. Erigon's domain pruner fires ~501 k blocks events per host per week; Nethermind's Hybrid pruner runs continuously at ~1,497 nodes/sec/host; Geth path-mode prunes implicitly; Ethrex has no pruning metric at all.
  • Reth is still in initial staged sync: its checkpoint sits ~70 to 130 k blocks behind the tip and is not advancing, so it is excluded from the synced comparisons.
  • From Nimbus, three of five synced ECs cross the 4-second deadline on P99 engine_newPayload (Nethermind 4.44 s, Geth 4.67 s, Ethrex ≥5.00 s), but the ranking flips by consensus client. It is a pairing property, not a fixed EC property.
  • Nethermind's spikes trace to two deliberate v1.37.1 choices. nethermind_pruning_time spikes past 5 s, over the deadline, entirely from the in-memory pruner.
  • Sync-committee duties get hit before attestations. A late sync-committee message misses the next block's sync_aggregate outright; a late attestation still has one slot of grace.