Skip to content

Comments

feat(consume): initial implementation of enginex#1

Closed
danceratopz wants to merge 4 commits intoconsume-enginexfrom
consume-enginex-sequential
Closed

feat(consume): initial implementation of enginex#1
danceratopz wants to merge 4 commits intoconsume-enginexfrom
consume-enginex-sequential

Conversation

@danceratopz
Copy link
Owner

@danceratopz danceratopz commented Nov 26, 2025

🗒️ Description

This PR adds an initial implementation of a consume enginex simulator that reuses client instances across tests sharing the same pre-allocation state to optimize test execution.

This PR does have basic pytest-xdist support which will be optimized in subsequent PRs.

Pending optimization: A huge performance gain is made if this PR is rebased on @marioevz's "optimize fill" PR ethereum#1804. For example, this command of 20 tests over two groups runs ~3x faster:

uv run consume enginex --input=https://github.com/danceratopz/execution-specs/releases/download/v0.1.0a1/fixtures.tar.gz -v -s -k "/shanghai/ and (fork_shanghai or fork_cancun) and push0"

This is because as-is, the genesis state root is (unnecessarily) calculated for every access of the pre-alloc group.

Note on fixtures:

  1. Extended testing of this PR doesn't make a lot of sense, as some largish changes will come when xdist gets optimized.
  2. The custom release above:
    • Only contains EEST tests --until Prague (no static tests).
    • Removes separate groups for all EEST tests marked as slow, which reduces the number of pre-alloc groups from 23938 to 312. This change will be PR'd independently. You can get this group information from the release, for example, via:
      uv run groupstats ~/.cache/ethereum-execution-spec-tests/cached_downloads/ethereum/execution-spec-tests/v5.3.0/fixtures_stable/fixtures/blockchain_tests_engine_x/pre_alloc/
      

Overview

The enginex simulator groups tests by pre_hash (hash of genesis + pre-state). Tests in the same group run against a single client instance instead of starting/stopping per test.

Key Components

New Files

  • simulators/enginex/conftest.py - Client fixture with reuse logic, test sorting by group
  • simulators/multi_test_client.py - MultiTestClientManager for client lifecycle, parallel fixtures to single_test_client.py
  • simulators/helpers/test_tracker.py - Tracks test completion per group for cleanup
  • fixtures/pre_alloc_groups.py - PreAllocGroup model for shared genesis/pre-state

Modified

  • consume.py - Adds xdist_group marker based on pre_hash for enginex fixtures
  • test_via_engine.py - Shared test logic between engine and enginex
  • pytest_hive.py - shared_hive_test fixture for cross-test client sharing

Flow

  1. Collection: pytest_collection_modifyitems counts tests per group, sorts by pre_hash
  2. First test in group: Starts client, registers with manager
  3. Subsequent tests: Reuses existing client
  4. Last test in group: mark_test_completed triggers client shutdown
  5. Session end: Cleanup any remaining clients

Fixture Chain

pre_alloc_group (cached) → client_genesis (cached) → buffered_genesis → client_files
                        → environment (cached)     ↘
                                                    → client (reused per group)

Usage

To test, you can use the recommended bootleg release here: https://github.com/danceratopz/execution-specs/releases/tag/v0.1.0a1

uv run consume enginex --input=https://github.com/danceratopz/execution-specs/releases/download/v0.1.0a1/fixtures.tar.gz -v -s -k "/shanghai/ and (fork_shanghai or fork_cancun) and push0"

🔗 Related Issues or PRs

N/A.

✅ Checklist

  • All: Ran fast tox checks to avoid unnecessary CI fails, see also Code Standards and Enabling Pre-commit Checks:
    uvx tox -e static
  • All: PR title adheres to the repo standard - it will be used as the squash commit message and should start type(scope):.
  • All: Considered adding an entry to CHANGELOG.md. skipped
  • All: Considered updating the online docs in the ./docs/ directory.

Copy link
Owner Author

@danceratopz danceratopz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More xdist code landed in this PR than intended. Indeed, this should run correctly with xdist if loadgroup is provided to xdist's distribution flag:

-n 8 --dist=loadgroup

The xdist_group test markers, which set the xdist group to be the test's pre-alloc group hash, provide an elegant way of keeping track of test execution, so I decided to leave them in this initial PR. The purpose of these markers in xdist is to enforce that one group (which is equivalent to execution on one client, in this PR) is distributed to the same worker, not amongst multiple workers. It could even work without (if no fcu is applied), but this approach feels safest/more optimal with regards to loading resources (e.g., genesis, env).

The caching of genesis, env,.. is not necessary until we perform further optimizations, but I don't think it's too confusing if the reviewer bears in mind what's coming: We will enforce a max-group size on the pre-alloc groups and further divide them based on that parameter. This allows for better distribution of (our very skewed group distribution; some very large, many very small) groups across workers, which helps avoid worker starvation.

f"BlockchainEngineXFixture test case '{test_case.id}' missing pre_hash"
)
group_identifier = test_case.pre_hash
marks.append(pytest.mark.xdist_group(name=group_identifier))
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We add this xdist-specific marker now, even in sequential mode, as it's an elegant solution to help track the count of tests pending/executed in each pre-alloc group (in order to terminate the client upon completion of all collected tests in the group).

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: This PR now contains basic, unoptimized xdist support.

Comment on lines +174 to +189
@pytest.fixture(scope="session")
def pre_alloc_group_cache() -> Dict[str, PreAllocGroup]:
"""Cache for pre-allocation groups to avoid reloading from disk."""
return {}


@pytest.fixture(scope="session")
def client_genesis_cache() -> Dict[str, dict]:
"""Cache for client genesis configs to avoid redundant to_json calls."""
return {}


@pytest.fixture(scope="session")
def environment_cache() -> Dict[str, dict]:
"""Cache for environment configs to avoid redundant computation."""
return {}
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, these snuck in here, these caches are not necessary in sequential mode, where each pre-alloc group only gets executed once.

This caching is actually only useful when splitting large pre-alloc groups into smaller ones (we do this in xdist mode to allow better distribution of groups amongst workers and avoid worker starvation).

@danceratopz danceratopz force-pushed the consume-enginex-sequential branch from be91a38 to 479d579 Compare November 27, 2025 10:06
@danceratopz danceratopz changed the title feat(consume): initial sequential implementation of enginex feat(consume): initial implementation of enginex Nov 27, 2025
@danceratopz danceratopz force-pushed the consume-enginex-sequential branch from 479d579 to e47a3cc Compare November 27, 2025 10:11
danceratopz pushed a commit that referenced this pull request Dec 1, 2025
…m#1666)

* refactor(tests): Refactor json_infra using `pytest_collect_file`

* fix(tests): json collecting

* fix(tests): blockchain test execution

* fix(tests): blockchain test execution

* refactor(tests): Refactor types in json_infra

* fix(tests): json_infra, imports, parse `exceptions` in some tests

* refactor(tests): move some definitions

* fix(tox.ini): Remove `--ignore-glob`

* fix(tests): workaround for FileNotFoundError

* fix(tests): revamp cache

fix(tests): Don't cache fixtures

Try to implement cache

Fix caching

feat(tests): Manage cache during execution

* fix(tox): Use `--dist=loadfile`

* fix(tests): json files cache

* Run selective tests based on changed files (#1)

* fix(tests): remove evm_tools marker from blockchain tests

* remove coverage from json_infra

* enhance(tools): add json_test_name to Hardfork

* fix(tests): handle failing transactions in state tests

* enhance(tests): add from and until fork option to json_infra

* enhance(tests): run json_infra selectively

* enhance(tests): subclass Hardfork

* bug(tests): run all tests for t8n changes

* enhance(tests): minor fix

* fix(tests): ignore expectSection tests and add coverage

* enhance(tests): refactor exception markers

This commit refactors exception markers and marks the EEST static tests as slow

* fix(tests): provide unique name to tests

* fix(tests): post review changes

* fix(tests): set BASE_SHA to merge base

---------

Co-authored-by: Guruprasad Kamath <48196632+gurukamath@users.noreply.github.com>
Co-authored-by: Guruprasad Kamath <guru241987@gmail.com>
@danceratopz danceratopz closed this Dec 4, 2025
danceratopz pushed a commit that referenced this pull request Jan 8, 2026
…saka-suggestions

fix(test-benchmark): update gas constants in calculations and comments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant