-
Notifications
You must be signed in to change notification settings - Fork 2
Feature/prewarming cache during txn simulation #92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…-simulator function
…he prearmed-cache or not
| ) -> Result<CachedReads, SimulationError> { | ||
| let mut merged_cache = CachedReads::default(); | ||
|
|
||
| for (idx, tx) in transactions.into_iter().enumerate() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be done concurrently?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is to be changed to happen concurrently. on it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe can use rayon parallel iterator?
| chain_spec: Arc<ChainSpec>, | ||
| } | ||
|
|
||
| impl<Client, Evm> TransactionSimulator<Client, Evm> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there are several flaws with existing approach
- simulate set can differ from the actual txs executed
- Storage Slots Not Cached
- simulation is sequential
we can try a different strategy
Instead of caching account balances and storage values (which become stale), cache which addresses and storage slots
each transaction will touch. At build time, use these access patterns to batch-fetch fresh state from the database.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pool.add_transaction()
↓
Transaction validated & added to pool
↓
Returns hash to user ✓
↓
Trigger simulation (fire-and-forget)
↓
[mpsc::unbounded_channel]
↓
┌────┬────┬────┬────┐
Worker 1 Worker 2 Worker 3 Worker 4
↓ ↓ ↓ ↓
Simulate (EVM execution - read-only)
↓ ↓ ↓ ↓
Extract keys (accounts, storage slots)
↓ ↓ ↓ ↓
Store in PreWarmedCache
this is planned design
|
need to add an engine integration test to ensure it is working properly |
PR: Add Cache Pre-Warming Infrastructure
Overview
Add two-level cache to reduce database queries during block building.
flowchart TB subgraph "Current Implementation" A[Block Building] --> B[cached_reads<br/>previous block] B --> C[Database] B -.->|cache miss| C end subgraph "Target Architecture" D[Background Job<br/>continuous loop] -.->|update| E[pre_warmed cache] D -->|get top N| F[Mempool] D -->|simulate| G[Pre-load state] H[Block Building] --> E E -->|L1: check| I{hit?} I -->|yes| J[Return] I -->|no| K[cached_reads<br/>previous block] K -->|L2: check| L{hit?} L -->|yes| J L -->|no| M[Database] M --> J endLookup order: pre_warmed (mempool) -> cached_reads (previous block) -> database
Sequence Diagram
Current Flow (Before This PR)
sequenceDiagram participant Builder as OpPayloadBuilder participant CacheDB as CachedReadsDbMut participant Base as cached_reads participant DB as Database Builder->>CacheDB: build(cached_reads.as_db_mut(state)) CacheDB->>Base: basic(address)? alt Account in cache Base-->>CacheDB: Return cached account else Cache miss Base->>DB: Query database DB-->>Base: Account data Base-->>CacheDB: Return account endNew Flow (After This PR)
sequenceDiagram participant BG as Background Job<br/>(TODO) participant Sim as TransactionSimulator participant Gen as BasicPayloadJobGenerator participant Builder as OpPayloadBuilder participant PreDB as PreWarmedCachedReadsDbMut participant PreCache as pre_warmed participant BaseCache as cached_reads participant DB as Database Note over BG,Gen: Background Pre-warming (Every 6s) BG->>Gen: pool.best_transactions().take(50) BG->>Sim: simulate_transactions(txs, parent) Sim-->>BG: CachedReads BG->>Gen: set_pre_warmed(PreWarmedCache) Note over Gen: Merges with existing cache<br/>if not stale Note over Gen,DB: Block Building Builder->>Gen: new_payload_job(attributes) Gen->>Gen: maybe_pre_warmed(parent_hash) alt Cache valid Gen-->>Builder: Some(pre_warmed) else Cache stale/missing Gen-->>Builder: None end alt Pre-warmed available Builder->>PreDB: PreWarmedCachedReadsDbMut::new(pre_warmed, cached_reads, state) Builder->>PreDB: build(db) PreDB->>PreCache: basic(address)? alt L1: Hit in pre_warmed PreCache-->>PreDB: Return cached else L1 miss PreDB->>BaseCache: basic(address)? alt L2: Hit in cached_reads BaseCache-->>PreDB: Return cached else L2 miss BaseCache->>DB: Query database DB-->>BaseCache: Account data BaseCache-->>PreDB: Return data end end else Pre-warmed unavailable (fallback) Builder->>BaseCache: cached_reads.as_db_mut(state) Builder->>BaseCache: build(db) Note over BaseCache,DB: Same as current flow endCore Changes
1. Two-Level Cache Lookup
Package:
reth-revmFile:
crates/revm/src/cached.rsAdded
PreWarmedCachedReadsDbMutstruct for two-level cache lookup:2. Transaction Simulator
Package:
reth-basic-payload-builderFile:
crates/payload/basic/src/simulator.rs(new)Simulates transactions to pre-load state:
CachedReadsfor pre-warmingWhy in
reth-basic-payload-builder?3. Pre-Warming Configuration
Package:
reth-basic-payload-builderFile:
crates/payload/basic/src/lib.rsAdded configurable pre-warming:
Feature Flag (
enabled):false: All pre-warming logic disabled (no cache updates, no lookups)false(safe for production until background job implemented)Added
PreWarmedCachestruct:4. Payload Builder Integration
Package:
reth-optimism-payload-builderFile:
crates/optimism/payload/src/builder.rsUses two-level cache when available:
Graceful fallback if pre-warmed cache unavailable.
Other Files
Integration Updates
crates/payload/basic/src/stack.rs- Pass pre-warmed cache throughcrates/ethereum/payload/src/lib.rs- Compatibility with new structureexamples/custom-engine-types/src/main.rs- Example compatibilityDependencies
crates/payload/basic/Cargo.toml- Added 6 workspace dependencies (all internal)Key Design Decisions
1. Time-Based Staleness (Not Block-Based)
Pre-warmed cache valid until timeout, not tied to specific parent block.
interval_secs * 2(derived from config)2. No Transaction Deduplication
Background job re-simulates top N every cycle, no tracking.
extend())3. Cache Replacement (Not Merging)
New simulations replace existing cache entirely.
4. Configurable Transaction Count
Default 50 based on X Layer reality (typical blocks: 1-50 tx).
Configuration
Default Values (X Layer Optimized)
Feature Control
enabled flag:
false(default):maybe_pre_warmed()returnsNoneimmediatelyset_pre_warmed()is no-op (ignores updates)true:Production safety:
falseuntil background job implemented and validatedHow staleness works:
interval_secs * staleness_multiplierPreWarmingConfigmaybe_pre_warmed()andset_pre_warmed()methodsTesting
All tests passing:
reth-basic-payload-builder: 3/3 passed (including feature flag tests)reth-revm(cache tests): 2/2 passedreth-optimism-payload-builder: 26/26 passedFeature flag tests verify:
enabled = false)Files Changed
reth-revmcrates/revm/src/cached.rsPreWarmedCachedReadsDbMutstruct withDatabasetrait impl for two-level lookup (pre-warmed → base → database)reth-basic-payload-buildercrates/payload/basic/src/lib.rsPreWarmingConfigstruct withenabledflag,PreWarmedCachestruct, updatedBasicPayloadJobGeneratorConfigwithpre_warmingfield, addedset_pre_warmed()andmaybe_pre_warmed()methods with enabled checksreth-basic-payload-buildercrates/payload/basic/src/simulator.rsTransactionSimulatorstruct withsimulate_transaction()andsimulate_transactions()methods to pre-load sender/recipient accountsreth-optimism-payload-buildercrates/optimism/payload/src/builder.rsbuild_payload()to usePreWarmedCachedReadsDbMutwhenpre_warmedis Some, else fallback to existingcached_reads.as_db_mut()reth-basic-payload-buildercrates/payload/basic/src/stack.rsBuildArgumentsdestructuring to includepre_warmedfieldreth-ethereum-payload-buildercrates/ethereum/payload/src/lib.rsBuildArguments::new()call to includepre_warmed: Noneexample-custom-engine-typesexamples/custom-engine-types/src/main.rsBuildArgumentsdestructuring to includepre_warmedfieldreth-basic-payload-buildercrates/payload/basic/Cargo.tomlreth-chainspec,reth-evm,reth-primitives,reth-provider,revm,thiserrorTotal: 8 files (1 new, 7 modified)
Backward Compatibility
Production Safety Verification
When
enabled = false(Verified):Cache Lookups Disabled
maybe_pre_warmed()returnsNoneimmediately (lib.rs:139)cached_reads.as_db_mut(state)(builder.rs:246)Cache Updates Disabled
set_pre_warmed()returns immediately without updating (lib.rs:168)Background Job (TODO - Next PR)
config.enabledin main loopComplete Shutdown Path:
What Gets Disabled:
What Stays Active:
Implementation Status
[DONE] Completed (This PR)
[TODO] Pending
Background Job (MUST check
enabledflag):Node Integration:
config.enabledin loopComplete shutdown when disabled:
enabledflag, skips simulation if falseset_pre_warmed()is no-op ifenabled = falsemaybe_pre_warmed()returnsNoneifenabled = falseUntil background job implemented,
enabled: falseby default (safe).