ci: run eest benchmark shards without a ramdisk - #22368
Merged
Merged
Conversation
The enginextests-benchmark-150m shards intermittently OOM-kill their
16GB hosted runner ("The runner has received a shutdown signal") — 5 of
the last 54 instances, always near the end of the test phase, on
unrelated branches. Peak demand measured for 150m: ~16.8GB = 9.9GB evm
process (live heap 4.9GB doubled by default GOGC; dominated by opReturn
return-data buffers and TemporalMemBatch write-set clones during the
unchunkified_bytecode tests) + 6.9GB MDBX datadir on the 8GB ramdisk,
whose tmpfs bytes compete with the process for the same RAM.
The ramdisk exists for shards that churn hundreds of short-lived
datadirs; the benchmark shards create 3 long-lived ones, so it buys no
wall time there. A/B on identical runners (run 29073818835): test phase
17m09s vs a 16m40s-17m06s five-run ramdisk baseline for 150m-parallel,
13m22s vs 11m14s-13m06s for 150m-sequential; both jobs passed with ~7GB
more headroom.
New manifest key no-ramdisk (opt-in true — a false-valued key would be
invisible to both jq's // default and GitHub expressions' loose ==, so
the enabled state must be the truthy one): the workflow skips the tmpfs
and puts those shards' datadirs under $RUNNER_TEMP, and
run-eest-spec-test.sh skips the Darwin auto-ramdisk, whose 2GB default
the 150m datadir could not fit anyway.
taratorio
requested review from
AskAlexSharov,
lystopad,
mriccobene and
yperbasis
as code owners
July 10, 2026 07:43
Contributor
There was a problem hiding this comment.
Pull request overview
This PR reduces flakiness in EEST engine-x benchmark CI shards by avoiding tmpfs/ramdisk usage for shards whose datadirs are large and long-lived, preventing RAM pressure that can OOM GitHub-hosted runners.
Changes:
- Add a
no-ramdiskper-shard flag totools/eest-spec-shards.ymland enable it for allenginextests-benchmark-*shards. - Update the EEST spec GitHub Actions workflow to disable the setup ramdisk for those shards and place datadirs under
$RUNNER_TEMPon disk. - Update
tools/run-eest-spec-test.shto honorno-ramdiskby skipping the Darwin auto-ramdisk for affected shards.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
tools/run-eest-spec-test.sh |
Parses the new manifest flag and skips Darwin auto-ramdisk when no-ramdisk is enabled. |
tools/eest-spec-shards.yml |
Documents and introduces no-ramdisk, enabling it for benchmark shards. |
.github/workflows/test-eest-spec.yml |
Disables the setup ramdisk for no-ramdisk shards and sets ERIGON_EXECUTION_TESTS_TMPDIR to $RUNNER_TEMP. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Don't export ERIGON_EXECUTION_TESTS_TMPDIR for them at all: with the setup-erigon ramdisk step skipped nothing else sets it (the TMPDIR override in setup-erigon is Windows-gated), so the shard exercises the same env-var-unset path as local runs and the datadirs land in the runner's default temp dir. /tmp and RUNNER_TEMP sit on the same root SSD on hosted runners, so the A/B timing evidence carries over.
yperbasis
approved these changes
Jul 10, 2026
yperbasis
left a comment
Member
There was a problem hiding this comment.
Approving. A few non-blocking nits:
- PR body: these
enginextests-benchmark-*shards are--timethroughput shards, so moving them off tmpfs puts SSD writeback into the measured path — their headline MGas/s will shift (pass/fail unaffected). Worth a line so the change isn't later read as a regression. test-eest-spec.yml: theramdisk:input uses!matrix.no-ramdiskwhileSHARD_NO_RAMDISKusesmatrix.no-ramdisk == true. They agree fortrue/absent, but diverge if the key is ever written as a quoted string.!(matrix.no-ramdisk == true)on the input line makes both derive from one predicate.test-eest-spec.yml(optional):make eest-spec-${{ matrix.shard }}interpolates an expression into arun:block. Pre-existing andmatrix.shardis trusted, but since you're already adding anenv:block,SHARD: ${{ matrix.shard }}+make eest-spec-"$SHARD"would align with the recent zizmor template-injection hardening (#22363).- Context (optional): a pointer to #22325's profiling comment + #22335 would preempt "why not also fix the code cache" — that's the FCU-less
TblCodeCachegrowth, handled separately and not needed here once the datadir is off RAM.
Review follow-up: interpolate matrix.shard through a step env var instead of directly into the run block, matching the cache-warming step's TARGET pattern and the template-injection hardening direction of #22363. matrix.shard is repo-controlled, so hygiene rather than a vulnerability fix.
Member
Author
|
Thanks for the review — all four nits addressed:
|
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.
Problem
The
eest-spec-enginextests-benchmark-150m-{parallel,sequential}jobs intermittently kill their runner — the job log ends withmake: *** Terminatedfollowed byThe runner has received a shutdown signal, the hosted-runner presentation of the VM exhausting memory. 5 of the last 54benchmark-150mjob instances died this way (e.g. this run on #22053, an unrelated branch the same morning, a sequential-variant instance), always at 95–100% of the test phase, independent of the PR under test.Profiling the 150m shard (locally, pinned to CI parallelism) shows peak demand of ~16.8 GB against the runner's 16 GB: a 9.9 GB evm process footprint (live heap 4.9 GB, roughly doubled by default GOGC; dominated by
opReturnreturn-data buffers andTemporalMemBatchwrite-set clones during thetest_unchunkified_bytecodecases, which allocate 10.6–12.1 GB each) plus a 6.9 GB MDBX datadir sitting on the 8 GB ramdisk — tmpfs bytes and process bytes compete for the same RAM. 1075 of the shard's 1077 tests share one(fork, preAllocHash)group, so a single node's datadir grows for essentially the whole run and the peak lands at the end. Whether a run survives comes down to GC timing and randomized test order — hence the flakiness.Prior investigation
#22325 (closed in favour of this PR) profiled the same OOM and pinned why the shard began flaking on Jul 7: the persistent code cache from #22154 grew the benchmark datadir by ~2 GB (5.65 → 7.40 GB peak), pushing the peak co-resident heap+datadir from ~12 GB to ~14+ GB — see the profiling comment. The FCU-less CodeStore growth is being capped separately in #22335; with the datadir off RAM, that growth no longer threatens the runner either way — the two changes are complementary.
Change
The ramdisk exists for shards that churn hundreds of short-lived datadirs, where create/unlink journaling dominates. The benchmark shards are the opposite shape (3 long-lived datadirs), so the ramdisk buys them no wall time — and costs them the RAM that OOMs the runner.
tools/eest-spec-shards.yml: new per-shard keyno-ramdisk: true, set on all 14enginextests-benchmark-*shards. Opt-in-true on purpose: afalse-valued key would be invisible to both jq's//default and GitHub expressions' loose==(null == falseis true there)..github/workflows/test-eest-spec.yml: honors the key — skips creating the tmpfs. Nothing else setsERIGON_EXECUTION_TESTS_TMPDIR/TMPDIRon Linux (the setup-erigon TMPDIR override is Windows-gated), so these shards exercise the env-var-unset path and their datadirs land in the runner's default temp dir (/tmp, on the same root SSD as$RUNNER_TEMP).tools/run-eest-spec-test.sh: the same key also skips the local (Darwin) auto-ramdisk for these shards — whose 2 GB default the 150m datadir (~7 GB) could not fit anyway. Local runs likewise fall through to the OS default temp dir.Non-benchmark shards are unchanged.
Wall-time evidence (A/B on identical runners)
Dispatched run 29073818835 — the 150m shards with datadirs on SSD, vs the five most recent green ramdisk runs:
Both A/B jobs passed, with ~7 GB more headroom at peak.
Benchmark semantics note: these are
--timethroughput shards, so datadirs-on-disk puts SSD writeback inside the measured path — per-test wall times (and any MGas/s derived from them) shift slightly at this PR's boundary; the A/B above bounds it at ~+1.8% for the 150m-parallel test phase. This is deliberate: production nodes run MDBX on SSD/NVMe with the OS page cache, so the post-PR numbers are more representative of real-world execution than tmpfs-backed ones. Treat pre-/post-PR timings as different baselines when comparing historical job logs.Verification
actionlint,shellcheck,bash -nclean;make lintclean.yq -o=jsonof the manifest) carriesno-ramdiskthrough tomatrix.*; row parsing verified for benchmark, stable, and race-regex shards.make eest-spec-enginextests-benchmark-1m-sequentialrun locally on Darwin through the new path: no auto-ramdisk created, datadirs in the default temp dir, all tests pass (1076/1076); a stable-shard control still creates the ramdisk.Create RAM diskstep is skipped and its log prints the default-tmpdir routing, while stable shards keeptmpdir: /mnt/erigon-ramdisk; the first CI Gate run had all eest shards green including both 150m jobs.No Go code changes; this is CI/tooling configuration, so the TDD cycle does not apply.