Skip to content

feat(l1,l2): adopt spawned v0.7 with one shutdown path, request timeouts, supervision, metrics and a testing roadmap - #7518

Draft
unbalancedparentheses wants to merge 14 commits into
mainfrom
spawned-v0.7
Draft

unbalancedparentheses wants to merge 14 commits into
mainfrom
spawned-v0.7

Conversation

@unbalancedparentheses

@unbalancedparentheses unbalancedparentheses commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Draft, not for merge yet. This is the single place where we post ethrex's spawned v0.7 adoption, the testing plan and every update (spawned releases, canary results, bugs the tests catch). Each commit stands alone. Replaces #7517 (testing roadmap), whose commits are included here.

Why

What changes

Commit What it does
chore(l1,l2): upgrade spawned to v0.7 Dependency bump only; no source change needed.
refactor(l1): own L1 actors in an ActorSystem with one shutdown path One ActorSystem (p2p group: peer table, tx broadcaster, RLPx initiator, subscription manager, discovery; peer table stops last). SIGINT, SIGTERM and fatal subsystem errors all go through shutdown_l1 with an 8 s deadline: cancel, snapshot peers (2 s), flush DB, save node_config.json, stop actors. A failed snapshot keeps the previously saved peers. The 1 s sleep is gone. A sync hitting a stopped actor during shutdown stops the cycle instead of exit(2). Missing the deadline names unfinished actors and exits 1.
fix(l1): give slow peer-table requests their own timeouts Peer selection 15 s, listings 10 s, shutdown snapshot 2 s; peer-table errors classified in one place (peer_table_error_is_recoverable).
feat(l1): supervise the discovery actor OneForOne, crash-only restarts, 3 per minute, backoff 1-10 s; other actors stay unsupervised (state not rebuilt, refs held elsewhere; reasons in init_l1). Optional fault injection: ETHREX_FAULT_INJECTION=1 + SIGUSR1 crashes discovery.
test(l1): pin the shutdown order against #7491 and mock the peer snapshot spawned-test mocks with paused time; reintroducing the old shutdown order fails the test every run.
feat(l1): export spawned actor metrics on /metrics 29 spawned_* metric families: 23 scalars, 3 histograms and 3 _max gauges.
test(l1): box the RPC dispatch future in the eth_callBundle tests Keeps a debug-build test under its stack limit (test helper only).
chore(l1,l2): pin spawned to main b1a17e6c plus spawned-test spawned v0.7.0 plus canary fixes and the not-yet-released spawned-test (lambdaclass/spawned#198).
docs(l1): …testing and verification roadmap (3 commits) docs/developers/l1/testing/roadmap.md and a Testing & Verification section in ROADMAP.md: testing ethrex the way FoundationDB, TigerBeetle, Dropbox, etcd and AWS test their systems, tied to real incidents. Priorities are proposals.

Measured so far (local, debug builds)

Run Before After
--dev, SIGINT ×5 1024-1026 ms 22-25 ms
hoodi with p2p, 45 s warm-up, ×3 1025-1026 ms 24-25 ms

Fault injection on hoodi: two injected discovery crashes gave generations 2 and 3, then a 23 ms shutdown. A canary on a synced node with a consensus client is next; results will be posted here.

Follow-ups

  • L2 is upgraded mechanically only; the sequencer still has its own Ctrl-C path and 1 s sleep.
  • ethlambda uses ethrex's p2p crate; once it moves to a revision with this branch it drops its duplicate spawned 0.5 (its own adoption PR: Adopt spawned v0.7: ActorSystem shutdown, supervision, test mocks, metrics and a testing roadmap ethlambda#783).
  • Most dummy_peer_handler-style test setups can't use mocks yet because PeerTable is a concrete ActorRef<PeerTableServer>; spawned tracks concrete-ref mocking.
  • spawned friction found here (group on an existing runtime, names in shutdown reports, timeouts on erased refs, process-global metrics config) is tracked in spawned.

Background: spawned roadmap, database-grade testing program.

Move spawned-concurrency and spawned-rt from 0.5.0 to v0.7.0, and add the
spawned-test crate as a workspace dependency for the actor tests that follow.

All three point at one git revision (39be3f3b: the v0.7.0 release, 02b9400b,
plus the unreleased spawned-test crate, lambdaclass/spawned#198), so they
share one set of types. Move to the crates.io release once spawned-test is
published.

Mechanical only: 0.5 protocol and handler signatures are unchanged in 0.7, and
no source change is needed. Behavior to keep in mind (from 0.6): once an actor
is stopped its queued messages can be discarded and pending requests return
`ActorStopped`. Ethrex already treats `ActorStopped` from the peer table as
"p2p is shutting down".

ethlambda depends on ethrex-p2p, so this lets it drop its duplicate spawned
0.5 once it moves to a revision with this change.
Shutdown was wired by hand and raced itself. The peer table, the RLPx
initiator and discovery each stopped on their own ctrl-c listener, while the
main path cancelled the node's token, asked the peer table for its connected
peers (saving an empty list if it had already stopped), flushed the database
and slept 1 s. A sync cycle that saw the stopped peer table before the token
was cancelled took `ActorStopped` as fatal and called `exit(2)`, skipping the
flush; the same race exited the ethrex_tests binary in 6 of 25 failed CI runs
(#7491).

Now one spawned `ActorSystem`, owned by `L1Node`, holds the node's long-lived
actors on a `p2p` async group sized like the default Tokio runtime: the peer
table, the tx broadcaster, the RLPx initiator, the WebSocket subscription
manager and discovery, admitted in that order so the peer table, which the
others call, stops last. No actor listens for ctrl-c any more. SIGINT, SIGTERM
and a fatal subsystem all lead to `shutdown_l1`, which, under an 8 s deadline
(Docker gives 10 s):

1. cancels the node's token, so sync, RPC and the block producer stop first;
2. snapshots the connected peers while the peer table still runs, keeping the
   previously saved peers if it cannot answer within 2 s instead of saving an
   empty list;
3. flushes the database;
4. saves node_config.json;
5. stops the owned actors with what is left of the deadline (at least 1 s).

A shutdown that misses the deadline logs the unfinished groups and actors by
name and exits 1 rather than wait on them with no bound; the database is
already flushed by then. The 1 s sleep is gone: it waited on nothing.

The sync cycle now decides through `SyncError::failure_action`: once the node
is shutting down, a failed cycle stops (`StopForShutdown`) instead of exiting,
and the sync loop does not start another. Outside shutdown, `ActorStopped`
stays fatal.

Per-connection actors stay outside the system: they are created per peer and
spawned does not have a dynamic supervisor yet. The plain `spawn` functions
remain for L2, the partial node, tests and ethrex-p2p's other users; they no
longer stop on ctrl-c either, so their owner decides when they stop. L2 dev
mode keeps the dev L1 node's `L1Node` alive for as long as it runs.
Every peer-table request carried spawned's hardwired five-second timeout. A
timed-out `get_best_peer` under mailbox pressure once exited the node (#6823);
that fix made the timeout recoverable, but the limit stayed five seconds for
every method, and a caller that gives up only sleeps 10 ms and asks again,
adding one more request to the same backed-up mailbox.

spawned v0.7 sets timeouts per protocol method. The peer table now uses:

- `PEER_SELECTION_TIMEOUT` (15 s) on `get_best_peer`, `get_best_peer_excluding`
  and `get_best_n_peers`: the sync download tasks' peer picks, which wait
  behind discovery and connection traffic. A slot reserved for a caller that
  already gave up is released when the reply is dropped (`RequestPermit`), so
  waiting longer leaks nothing.
- `PEER_LISTING_TIMEOUT` (10 s) on `get_peers_data` and
  `get_peer_diagnostics`: RPC admin and diagnostics listings that copy every
  peer.
- spawned's default five seconds for everything else. The shutdown path's
  peer snapshot already sets its own two seconds on the call.

`ActorError` from the peer table is now classified in one place,
`peer_table_error_is_recoverable`, with an exhaustive match, and used by every
path a peer-table error takes into the sync classifier: `SyncError` directly,
through `PeerHandlerError`, and through `SnapError::PeerTable`, which the
`SyncError::Snap(_) => false` catch-all used to make fatal whatever its
cause. Today only sends reach that variant (so only `ActorStopped`), but a
timeout there would have exited the node.

Tests use spawned-test mocks behind the protocol reference with paused time:
a pick or listing that replies after 6 s succeeds, another request times out
at 5 s, and each classification path retries on a timeout and exits on a
stopped table only while the node runs.
Put discovery under a OneForOne supervisor in the node's actor system. A panic
in a discovery handler used to stop discovery for the rest of the run: the
node kept the peers it had but found no new ones.

Discovery is the one long-lived p2p actor that restarts safely. Nothing holds
a reference to it (it is fed by its own UDP listener and timers), and its state
is soft: sessions, pending pings and lookups, IP votes. A fresh instance
rebuilds them from the peer table and the bootnodes, as at startup. Every
generation shares the UDP socket bound at startup; a stopped generation's
listener stops with it, so two never split the packets. A replacement starts
from the identity its predecessor published (its ENR with a new IP or fork id)
rather than the startup record.

Budget: Transient (a deliberate stop stays stopped), at most 3 restarts a
minute, backoff 1 s to 10 s. Discovery that keeps crashing exhausts it, the
supervisor gives up with an error, and the node runs on without discovery, as
it did before. Restarts and the give-up are logged by a watcher, since spawned
logs neither. The shutdown report names the current discovery generation.

The other actors stay unsupervised, with the reasons in `init_l1`: the peer
table holds the node's unpersisted view of the network (scores, sessions,
connection registrations, in-flight counts) and everything holds an `ActorRef`
to it; the tx broadcaster and RLPx initiator are referenced through
`P2PContext` and `PeerHandler`, which a replacement would not rebind; the
subscription manager owns the WebSocket subscribers. Supervising them needs
reference rebinding first.

For canary nodes only, `ETHREX_FAULT_INJECTION=1` makes SIGUSR1 crash the
current discovery actor with a real handler panic, to exercise recovery.
…shot

#7491 was a shutdown-order bug: the peer table stopped (on its own ctrl-c
listener) before the node's token was cancelled, so a sync cycle took the
`ActorStopped` it got as fatal and exited with code 2. The new tests watch the
peer table stop and classify that `ActorStopped` with the node's shutdown state
at that instant, as `Syncer::start_sync` does:

- `shutdown_l1` must leave it `StopForShutdown`, and must save the peers it
  snapshotted while the table still ran.
- A negative control runs the old order (actors stopped, then the token
  cancelled) and gets `Exit`. Swapping the two steps inside `shutdown_l1`
  fails the first test with `left: Exit`.

The peer snapshot now takes the peer table as a protocol reference
(`PeerTableServerRef`) with the timeout applied by the caller
(`peer_snapshot_source`), so tests stand a spawned-test mock in for a table
with live connections, which a real table only gets from completed RLPx
handshakes. They cover the connected peers being saved, and a slow or stopped
table keeping the previously saved peers instead of an empty list.

Most other ethrex tests that spawn a real peer table with a dummy P2P context
(`dummy_peer_handler`, the tx broadcaster and connection tests) keep doing so:
`PeerHandler` and `P2PContext` hold the concrete `ActorRef<PeerTableServer>`,
which a mock cannot stand in for without changing those types.
Enable spawned-concurrency's `metrics` feature with ethrex's `metrics`
feature, and append its Prometheus text to the `/metrics` response (new
`actors` feature of ethrex-metrics): actors spawned, active and exited by
reason; messages enqueued, handled and discarded; mailbox depth; request
outcomes (replied, timed out, actor stopped, cancelled); and queue-wait,
handler-duration and request-latency histograms. All series are prefixed
`spawned_` and summed over every actor in the process, with no per-actor
labels.

They are what to watch on a canary of the v0.7 runtime: a supervised
discovery restart shows as a panic exit and a new spawn, peer-table pressure
as queue wait and mailbox depth, and peer-selection timeouts in
`spawned_requests_timed_out_total`.

With `--metrics`, timings are sampled 1 in 16 per message kind
(`ACTOR_TIMING_SAMPLE_EVERY`): the peer table handles every discovery packet
and connection update, and timing all of them reads the clock twice per
message on the busiest actor. Without `--metrics` only the counters are kept.
Counters count every message either way. The sampling is set before the first
actor starts, as spawned requires.

Checked on a hoodi node: after 25 s, `/metrics` reported 215 actors spawned
(66 active, mostly RLPx connections), 1497 messages handled and 94 timing
samples.
`eth::call_bundle::tests::malformed_raw_transactions` overflowed the 2 MiB
test-thread stack in debug builds after the spawned upgrade (macOS aarch64).
It was already at the edge on origin/main: it needs between 1.95 MB and
2 MiB there, and 2.1-2.3 MB after the upgrade, whose request futures are
somewhat larger (`map_http_requests`' future grows from 33,728 to 35,168
bytes).

The test helper now awaits the dispatch future boxed, so its ~35 KB state
lives on the heap rather than in every debug poll frame. The whole module
then passes with 1.8 MB of stack. Test-only; RPC dispatch itself is unchanged.
Add a Testing & Verification section to ROADMAP.md with proposed
priorities, and docs/developers/l1/testing/roadmap.md covering current
suites, gaps from recent incidents, techniques used by database teams
and a phased plan with acceptance criteria.
…tion blockers and more incidents

Lead with per-incident failpoint tests that need no spawned work; make the
spawned v0.7 upgrade an explicit prerequisite; record what blocks simulation
(clock, RNG, sockets, process::exit) with file:line; split subsystem from
whole-node simulation; add five more incidents; refine reference-model,
metamorphic, Kani and TLA+ items; note what is shared with ethlambda.
@github-actions github-actions Bot added L1 Ethereum client L2 Rollup client labels Nov 9, 2026
@github-actions

github-actions Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

⚠️ Known Issues — intentionally skipped tests

Source: docs/known_issues.md

rpc-compat log-bearing cases excluded

Where: KNOWN_EXCLUDED_TESTS in .github/scripts/check-hive-results.sh counts out
eight hive rpc-compat cases — the four eth_getLogs cases, eth_getBlockReceipts/get-block-receipts-latest,
and three eth_getTransactionReceipt cases. They are exactly the cases whose recorded
response contains at least one log object; every case with an empty log array still runs.
Note this leaves eth_getLogs with no rpc-compat coverage at all, since all four of its
cases are in the set.

Why: ethrex populates blockTimestamp on log objects, as geth, besu, nethermind, reth
and erigon all do. hive's rpc-compat compares responses byte-exactly (jsondiff.FullMatch;
the lenient checkJSONStructure path applies only to cases upstream marks speconly), and
the corpus is pinned to execution-apis d08382ae (2025-02-10), whose recordings predate the
field — it entered the schema in execution-apis#639 and the fixtures in #846 (2026-07-22).
So the extra key cannot match, and this is a property of the pin rather than of the response.

The pin cannot move, and this is not temporary. The pin sits one commit before
execution-apis#627, which moved the test chain to a pre-merge genesis: the current corpus has
~36 proof-of-work blocks before its terminal total difficulty. ethrex does not support
pre-merge chains and will not, so importing that chain.rlp fails at block 1 —
validate_block_header has no pre-London base-fee path. Every revision carrying
blockTimestamp in its fixtures also carries that chain, so there is no revision that
satisfies both. Nor can the corpus be patched locally: rpc-compat's Dockerfile clones
ethereum/execution-apis by hard-coded URL, so the branch buildarg cannot point at a fork.

Coverage: the field itself is pinned by
block_timestamp_is_on_the_log_and_not_on_the_receipt in
crates/networking/rpc/types/receipt.rs, which asserts it is present on each log and absent
from the receipt level.

Removal: delete the entries if ethrex ever gains pre-merge chain import, or if upstream
marks these cases speconly so they are type-checked instead of compared byte-for-byte.


rpc-compat malformed storage key cases excluded

Where: KNOWN_EXCLUDED_TESTS in .github/scripts/check-hive-results.sh counts out
eth_getStorageAt/get-storage-invalid-key and eth_getStorageAt/get-storage-invalid-key-too-large.
daily_hive_report.yaml runs the same pin without that list, so both show as failures there,
as the log-bearing cases above do.

Why: both cases send a storage key that does not decode (0xasdf, and a key of 65 hex
digits). ethrex answers -32602: Invalid params, the code JSON-RPC 2.0 reserves for malformed
params. The pinned recordings expect geth's -32000; execution-apis#712 (2025-12-04)
re-recorded both as -32602. hive strips error messages but compares the code, so the pinned
recordings cannot match. The pin cannot move, for the reason given in the section above.

Coverage: the key decode error surfaces as RpcErr::BadParams, and the BadParams →
-32602 mapping is pinned by eth_get_block_access_list_rejects_extra_params in
test/tests/rpc/block_access_list_tests.rs.

Removal: delete the entries if the pin ever moves past execution-apis#712.


The stateless schema id does not identify the encoding

Where: STATELESS_INPUT_SCHEMA_ID in crates/common/types/stateless_ssz.rs.

Upstream keeps the stateless input schema id at 0x1501
(fork_index 0x15 << 8 | revision 0x01) across incompatible body changes. Four
encodings have now shipped under it: tests-zkevm@v0.6.2, then #3248 + #3278,
then #3356, which moved state, codes and public_keys from SszList to
ProgressiveList, then #3652 in tests-zkevm@v21.0.1, which removed
public_keys outright without bumping the revision. ethrex speaks the last one.

The consequence is that the 2-byte prefix cannot be used to detect a stale or
mismatched bundle. A wrong-dialect input is accepted by the id check and then
fails later — in SSZ decode, or on a root that does not match — rather than being
rejected up front for what it is. only_amsterdam_schema_id_decodes therefore
proves less than its name suggests.

Worth raising upstream: a revision field that does not move across a body change
provides no version negotiation at all.


ZisK guest program hash changes with the unsync_cell gate

Where: crates/common/types/block.rs, transaction.rs.

The gate on the single-threaded unsync_cell::OnceCell moved from
all(feature = "eip-8025", target_arch = "riscv64") to
all(feature = "zisk", target_arch = "riscv64") when the eip-8025 feature was removed.

The guest ELFs were previously built --features "<zkvm>-build-elf,ci", which never enabled
eip-8025, so they compiled the atomic once_cell variant. bin/zisk/Cargo.toml does enable
ethrex-common/zisk, so the ZisK guest now compiles the unsafe impl Sync cell instead.
That changes the ELF bytes and therefore the program hash and verification key.

This is intended (the guest is single-threaded, so the unsync cell is sound and cheaper), but it
is a VK change rather than a no-op refactor, and the diffstat presents it as a file rename
(eip8025_cell.rs → unsync_cell.rs). Anyone pinning a ZisK VK across this change must
re-register it. The stateless-validator crate now forwards ethrex-common/zisk from its own
zisk feature so the two ZisK guests do not disagree on the cell type.


Release signing key is an unprotected repository secret

Where: .github/workflows/tag_release.yaml.

MINISIGN_SECRET_KEY is a plain repository secret. There is no environment: on
finalize-release or dry-run-release-assets, and gh api repos/lambdaclass/ethrex/rulesets
shows only branch-targeted rulesets, so the github.ref_type == 'tag' condition is a workflow
check rather than an enforced boundary: anyone who can push a tag can reach the signing key.

This is a repository-settings change, not a code change, so it is recorded here rather than
fixed in the tree. Recommended:

  1. Move MINISIGN_SECRET_KEY / MINISIGN_PASSWORD into a GitHub Environment with required
    reviewers, and add environment: to the two jobs that sign.
  2. Add a ruleset targeting refs/tags/v* restricting who may create release tags.

Until then, the compromise of that key is silent and durable: signatures would still verify
against the committed .github/minisign.pub.

@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown

Lines of code report

Total lines added: 646
Total lines removed: 33
Total lines changed: 679

Detailed view
+------------------------------------------------------+-------+------+
| File                                                 | Lines | Diff |
+------------------------------------------------------+-------+------+
| ethrex/cmd/ethrex/ethrex.rs                          | 153   | -33  |
+------------------------------------------------------+-------+------+
| ethrex/cmd/ethrex/initializers.rs                    | 1396  | +94  |
+------------------------------------------------------+-------+------+
| ethrex/cmd/ethrex/l2/command.rs                      | 660   | +3   |
+------------------------------------------------------+-------+------+
| ethrex/cmd/ethrex/l2/initializers.rs                 | 561   | +7   |
+------------------------------------------------------+-------+------+
| ethrex/cmd/ethrex/lib.rs                             | 9     | +1   |
+------------------------------------------------------+-------+------+
| ethrex/cmd/ethrex/partial/mod.rs                     | 361   | +2   |
+------------------------------------------------------+-------+------+
| ethrex/cmd/ethrex/shutdown.rs                        | 118   | +118 |
+------------------------------------------------------+-------+------+
| ethrex/cmd/ethrex/utils.rs                           | 240   | +26  |
+------------------------------------------------------+-------+------+
| ethrex/crates/blockchain/metrics/api.rs              | 64    | +5   |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/p2p/discovery/mod.rs        | 28    | +1   |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/p2p/discovery/server.rs     | 600   | +30  |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/p2p/discovery/supervised.rs | 255   | +255 |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/p2p/network.rs              | 813   | +33  |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/p2p/peer_handler.rs         | 1038  | +5   |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/p2p/peer_table.rs           | 1760  | +13  |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/p2p/sync.rs                 | 476   | +31  |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/p2p/sync_manager.rs         | 283   | +3   |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/p2p/tx_broadcaster.rs       | 453   | +14  |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/rpc/subscription_manager.rs | 187   | +4   |
+------------------------------------------------------+-------+------+
| ethrex/crates/networking/rpc/test_utils.rs           | 598   | +1   |
+------------------------------------------------------+-------+------+

@unbalancedparentheses

Copy link
Copy Markdown
Contributor Author

Hoodi canary results (branch 044057c vs main 1dfaa09)

Release builds on a 256 GB Linux server. Lighthouse v8.3.3 with checkpoint sync. Snap sync from an empty datadir took about 25 min, with peak RSS 17.5 GB. Main then ran on the same synced datadir for comparison.

No regressions found. All shutdowns were clean, discovery supervision behaved as designed, and the branch used about the same resources as main.

Shutdown

run result
branch, 6 runs on the synced node (3 SIGTERM, 3 SIGINT) All exit 0. 5 runs took 17–27 ms (114–324 ms wall time). The first, right after sync, took 2.0 s for the DB flush. No deadline overruns. node_config.json always saved the live peer count.
branch, SIGTERM during snap sync exit 0 in 312 ms, 4 peers saved
main, 6 runs All exit 0, 1.10–1.19 s wall time (the fixed 1 s sleep). On SIGINT it saved 0 peers while connected to 11 and to 4 (the bug this PR's #7491 test covers).

Fault injection (ETHREX_FAULT_INJECTION=1, SIGUSR1 crashes discovery)

  • Three crashes 25 s apart: discovery restarted as generations 2, 3 and 4, each 0.8–1.2 s later, and kept running (FindNode sent went from 1955 to 5294).
  • Four crashes 8 s apart: generations 5–7, then Discovery supervisor gave up 3.9 s after the fourth.
    • Discovery stopped, but the node kept importing with 0 lag.
    • A SIGTERM afterwards was clean (37 ms).

Resources (steady state after restart)

branch main
CPU (median) 5–8% 3–8%
RSS 1.4–1.5 GB 1.3–1.7 GB
threads 145 112 (the branch's separate p2p execution group runtime adds about 33)
open fds about 460 about 450
lag behind CL head 0 blocks 0 blocks

Peers: lower peer counts looked like a regression at first. Two A/B runs started together, with the ports swapped between them, showed the gap follows the port and start order, not the binary.

Actor metrics (branch)

  • During sync: 108M messages at about 73k/s, mean queue wait 2 µs, max mailbox depth 2.
  • Steady state: about 360 msg/s, mean queue wait 56 µs, max 247 ms.
  • Throughout: 0 timed-out requests and 0 ActorStopped. The only panics were the 7 injected ones.
  • Correction to the description: /metrics exposes 29 spawned_* families (23 scalars, 3 histograms, 3 _max gauges), not 23 series. I've updated the description.

ethrex issues found along the way (not caused by this PR)

Next

  • Repin to spawned v0.7.1 once it's released, then rerun this canary.
  • Update the comment in discovery/supervised.rs: spawned now logs the give-up itself.

A soak of the branch is still running on Hoodi.

@unbalancedparentheses

Copy link
Copy Markdown
Contributor Author

Bumped spawned to 0.7.1 from crates.io (1146541), dropping the git pin. Also updated the stale comment in discovery/supervised.rs: 0.7.1 logs restarts at info and supervisor failure at warn.

Gates on Linux x86_64 (fede), toolchain 1.93.0:

Gate Result
ethrex_tests p2p:: 276/276
ethrex-p2p lib 161/161
ethrex lib 35/35
ethrex-rpc lib 331/331
cargo clippy --workspace --all-targets -D warnings clean
ethrex_tests rpc:: at the default 2 MiB debug stack stack overflow in rpc::estimate_gas_tests::estimate_gas_for_a_gas_observing_call_is_executable_and_within_the_tolerance

The rpc:: overflow is not a 0.7.1 regression: it fails identically on the previous pin (8b95117e), and the minimum passing stack is the same.

Minimum RUST_MIN_STACK that passes the rpc:: group (Linux, debug):

Build Min stack Margin vs 2 MiB
ethrex main (spawned 0.5.0) 1919 KiB +129 KiB
this branch + spawned 8b95117e 2055 KiB −7 KiB
this branch + spawned 0.7.1 2055 KiB −7 KiB

On macOS the same group needed ~2035 KB and passed, which is why earlier gates were green. The v0.7 upgrade costs ~136 KiB more stack in debug than main. We're investigating on the spawned side (future sizes on the request/protocol path) so the fix lands in spawned rather than in individual tests.

@unbalancedparentheses

Copy link
Copy Markdown
Contributor Author

Bumped spawned to 0.7.2 (crates.io). 0.7.2 makes ActorRef/Context one pointer again (lambdaclass/spawned#227), which fixes the debug rpc:: stack overflow reported above: the group's minimum stack goes from 2052 KiB to 1852 KiB (ethrex main on spawned 0.5 needed 1919 KiB), so it now passes at the default 2 MiB with ~196 KiB of headroom. spawned's new client-compatibility CI built this branch against the 0.7.2 code and ran check, clippy, the p2p:: and rpc:: groups and the p2p/rpc/ethrex lib tests: all green (lambdaclass/spawned actions run 42881805858). Also moved the stale spawned git pins in crates/l2/tee/quote-gen and tooling/zkevm_bench lockfiles to 0.7.2.

@unbalancedparentheses

Copy link
Copy Markdown
Contributor Author

Bumped spawned to 0.7.3 (crates.io). One supervision behaviour change, aligned with OTP: a failed restart (a replacement that stops before readiness) is retried within the restart budget instead of failing the supervisor, and OneForAll rebuilds the whole cohort; see migration notes. If a supervised child here relied on a failing replacement to stop its tree, it should call Supervisor::stop/shutdown explicitly. spawned's client-compatibility CI was green for this branch against the 0.7.3 code before the release.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L1 Ethereum client L2 Rollup client

Projects

Status: No status
Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant