-
Notifications
You must be signed in to change notification settings - Fork 201
feat(l1): periodic mempool sweep for stale and dormant transactions #6610
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
base: main
Are you sure you want to change the base?
Changes from 5 commits
1e32b51
1911e8b
6be47b1
3ad5d55
db48da6
c3184c1
f45cdba
ee900a3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,8 +52,8 @@ pub mod vm; | |
|
|
||
| use ::tracing::{debug, error, info, instrument, warn}; | ||
| use constants::{ | ||
| AMSTERDAM_MAX_INITCODE_SIZE, MAX_INITCODE_SIZE, MAX_TRANSACTION_DATA_SIZE, | ||
| POST_OSAKA_GAS_LIMIT_CAP, | ||
| AMSTERDAM_MAX_INITCODE_SIZE, DEFAULT_DORMANCY, DEFAULT_MAX_NONCE_GAP, DEFAULT_MEMPOOL_LIFETIME, | ||
| MAX_INITCODE_SIZE, MAX_TRANSACTION_DATA_SIZE, MEMPOOL_SWEEP_INTERVAL, POST_OSAKA_GAS_LIMIT_CAP, | ||
| }; | ||
| use error::MempoolError; | ||
| use error::{ChainError, InvalidBlockError}; | ||
|
|
@@ -100,7 +100,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; | |
| use std::sync::LazyLock; | ||
| use std::sync::mpsc::Sender; | ||
| use std::sync::{ | ||
| Arc, RwLock, | ||
| Arc, RwLock, Weak, | ||
| atomic::{AtomicBool, AtomicUsize, Ordering}, | ||
| mpsc::{Receiver, channel}, | ||
| }; | ||
|
|
@@ -231,6 +231,17 @@ pub struct BlockchainOptions { | |
| /// warmer thread and the executor. Set to false (via `--no-precompile-cache`) to | ||
| /// disable the cache for benchmarking purposes. | ||
| pub precompile_cache_enabled: bool, | ||
| /// Maximum age of a mempool transaction before it is evicted by the | ||
| /// periodic TTL sweep. | ||
| pub mempool_lifetime: Duration, | ||
| /// Threshold for the dormancy sweep: senders whose top pending nonce | ||
| /// exceeds their on-chain nonce by more than this value are eligible | ||
| /// for eviction once they have been dormant for [`Self::dormancy`]. | ||
| pub max_nonce_gap: u64, | ||
| /// Dormancy window used by the nonce-gap sweep. A sender is only evicted | ||
| /// when all their pool entries are older than this and the nonce gap is | ||
| /// above [`Self::max_nonce_gap`]. | ||
| pub dormancy: Duration, | ||
| } | ||
|
|
||
| impl Default for BlockchainOptions { | ||
|
|
@@ -242,6 +253,9 @@ impl Default for BlockchainOptions { | |
| max_blobs_per_block: None, | ||
| precompute_witnesses: false, | ||
| precompile_cache_enabled: true, | ||
| mempool_lifetime: DEFAULT_MEMPOOL_LIFETIME, | ||
| max_nonce_gap: DEFAULT_MAX_NONCE_GAP, | ||
| dormancy: DEFAULT_DORMANCY, | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -355,6 +369,23 @@ impl Blockchain { | |
| } | ||
| } | ||
|
|
||
| /// Spawn the periodic mempool sweep task. | ||
| /// | ||
| /// Every [`MEMPOOL_SWEEP_INTERVAL`] the task runs two evictions: | ||
| /// 1. TTL sweep: drops txs older than [`BlockchainOptions::mempool_lifetime`]. | ||
| /// 2. Dormancy sweep: drops every entry belonging to senders whose top | ||
| /// pending nonce exceeds the on-chain nonce by more than | ||
| /// [`BlockchainOptions::max_nonce_gap`] and who have been dormant | ||
| /// for at least [`BlockchainOptions::dormancy`]. | ||
| /// | ||
| /// The task holds only a [`Weak`] to the `Blockchain`, so it exits as | ||
| /// soon as the last `Arc` is dropped — there is no need to thread a | ||
| /// cancellation token through the call sites. | ||
| pub fn spawn_mempool_sweep(self: &Arc<Self>) { | ||
| let weak = Arc::downgrade(self); | ||
| tokio::spawn(run_mempool_sweep(weak)); | ||
| } | ||
|
|
||
| /// Executes a block withing a new vm instance and state | ||
| fn execute_block( | ||
| &self, | ||
|
|
@@ -3166,3 +3197,52 @@ fn collect_trie(index: u8, mut trie: Trie) -> Result<(Box<BranchNode>, Vec<TrieN | |
| }; | ||
| Ok((root, nodes)) | ||
| } | ||
|
|
||
| /// Periodic mempool sweep loop: ticks every [`MEMPOOL_SWEEP_INTERVAL`] and | ||
| /// runs the TTL + dormancy evictions on the live mempool. | ||
| /// | ||
| /// Holds only a [`Weak`] to the [`Blockchain`] so the task tears itself down | ||
| /// once the last strong reference is dropped (no explicit shutdown signal). | ||
| async fn run_mempool_sweep(blockchain: Weak<Blockchain>) { | ||
| let mut interval = tokio::time::interval(MEMPOOL_SWEEP_INTERVAL); | ||
| // `Skip` so a sweep iteration that takes longer than the configured | ||
| // interval doesn't trigger a burst of back-to-back ticks afterward | ||
| // (default `Burst` behavior). Under load we'd rather skip a sweep cycle | ||
| // than queue them up. | ||
| interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); | ||
| // Drop the first immediate tick — let the node finish initialising before | ||
| // doing any work. | ||
| interval.tick().await; | ||
| loop { | ||
|
Comment on lines
+3244
to
+3253
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| interval.tick().await; | ||
| let Some(chain) = blockchain.upgrade() else { | ||
| // Blockchain dropped; tear down. | ||
| return; | ||
| }; | ||
|
|
||
| let ttl = chain.options.mempool_lifetime; | ||
| let max_gap = chain.options.max_nonce_gap; | ||
| let dormancy = chain.options.dormancy; | ||
|
|
||
| match chain.mempool.evict_stale(ttl) { | ||
| Ok(0) => {} | ||
| Ok(n) => info!(evicted = n, ttl_secs = ttl.as_secs(), "mempool TTL sweep"), | ||
| Err(e) => warn!(error = %e, "mempool TTL sweep failed"), | ||
| } | ||
|
|
||
| match chain | ||
| .mempool | ||
| .evict_dormant(&chain.storage, max_gap, dormancy) | ||
| .await | ||
| { | ||
| Ok(0) => {} | ||
| Ok(n) => info!( | ||
| evicted = n, | ||
| max_nonce_gap = max_gap, | ||
| dormancy_secs = dormancy.as_secs(), | ||
| "mempool dormancy sweep" | ||
| ), | ||
| Err(e) => warn!(error = %e, "mempool dormancy sweep failed"), | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,3 +52,24 @@ pub const MIN_GAS_LIMIT: u64 = 5000; | |
| // === EIP-7825 constants === | ||
| // https://eips.ethereum.org/EIPS/eip-7825 | ||
| pub const POST_OSAKA_GAS_LIMIT_CAP: u64 = 16777216; | ||
|
|
||
| // === Mempool sweep defaults === | ||
|
|
||
| use std::time::Duration; | ||
|
|
||
| /// Default maximum age of a mempool transaction before the periodic sweep | ||
| /// evicts it. Transactions older than this are dropped regardless of pool | ||
| /// occupancy. | ||
| pub const DEFAULT_MEMPOOL_LIFETIME: Duration = Duration::from_secs(3 * 60 * 60); | ||
|
|
||
| /// Default maximum gap allowed between a sender's top pending nonce and | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doc-vs-impl mismatch: this comment says "between a sender's top pending nonce and their on-chain nonce", but the Swap "top" → "lowest" here so the constant's docstring matches the implementation. Otherwise a future reader tuning |
||
| /// their on-chain nonce before the dormancy sweep considers them stalled. | ||
| pub const DEFAULT_MAX_NONCE_GAP: u64 = 64; | ||
|
|
||
| /// Default dormancy window for the nonce-gap sweep. A sender must have made | ||
| /// no on-chain progress for at least this long (i.e. all their pool entries | ||
| /// are older than this) before they are eligible for eviction. | ||
| pub const DEFAULT_DORMANCY: Duration = Duration::from_secs(3 * 60 * 60); | ||
|
|
||
| /// How often the periodic mempool sweep runs. | ||
| pub const MEMPOOL_SWEEP_INTERVAL: Duration = Duration::from_secs(60); | ||
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.
{secs}s, so both--mempool.lifetimeand--mempool.dormancydisplay as10800sin--helpand inCLI.md. Expressing the default as3hdirectly matches the format the parser already accepts and is far more readable to operators.Prompt To Fix With AI
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.
3ad5d55