|
2 | 2 |
|
3 | 3 | use clap::{builder::Resettable, Args}; |
4 | 4 | use reth_engine_primitives::{ |
5 | | - TreeConfig, DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE, DEFAULT_SPARSE_TRIE_MAX_STORAGE_TRIES, |
6 | | - DEFAULT_SPARSE_TRIE_PRUNE_DEPTH, |
| 5 | + TreeConfig, DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE, DEFAULT_PERSISTENCE_PRUNER_DELETE_LIMIT, |
| 6 | + DEFAULT_SPARSE_TRIE_MAX_STORAGE_TRIES, DEFAULT_SPARSE_TRIE_PRUNE_DEPTH, |
7 | 7 | }; |
8 | 8 | use std::{sync::OnceLock, time::Duration}; |
9 | 9 |
|
@@ -400,6 +400,25 @@ pub struct EngineArgs { |
400 | 400 | default_value = DefaultEngineValues::get_global().state_root_task_timeout.as_deref().unwrap_or("1s"), |
401 | 401 | )] |
402 | 402 | pub state_root_task_timeout: Option<Duration>, |
| 403 | + |
| 404 | + /// Maximum number of entries the persistence pruner may delete in a single run. |
| 405 | + /// Limits MDBX dirty page accumulation to prevent OOM when the pruner runs for the |
| 406 | + /// first time after startup on a large database. |
| 407 | + /// |
| 408 | + /// Set to 0 to disable the limit (unlimited). |
| 409 | + #[arg(long = "engine.persistence-pruner-delete-limit", default_value_t = DEFAULT_PERSISTENCE_PRUNER_DELETE_LIMIT)] |
| 410 | + pub persistence_pruner_delete_limit: usize, |
| 411 | + |
| 412 | + /// Timeout for the persistence pruner per run. Prevents a single prune from blocking |
| 413 | + /// block persistence for too long. |
| 414 | + /// |
| 415 | + /// CAUTION: Account and Storage History segments treat this as a soft limit. |
| 416 | + /// |
| 417 | + /// Set to 0s to disable. |
| 418 | + /// |
| 419 | + /// --engine.persistence-pruner-timeout 30s |
| 420 | + #[arg(long = "engine.persistence-pruner-timeout", value_parser = humantime::parse_duration)] |
| 421 | + pub persistence_pruner_timeout: Option<Duration>, |
403 | 422 | } |
404 | 423 |
|
405 | 424 | #[allow(deprecated)] |
@@ -464,6 +483,8 @@ impl Default for EngineArgs { |
464 | 483 | state_root_task_timeout: state_root_task_timeout |
465 | 484 | .as_deref() |
466 | 485 | .map(|s| humantime::parse_duration(s).expect("valid default duration")), |
| 486 | + persistence_pruner_delete_limit: DEFAULT_PERSISTENCE_PRUNER_DELETE_LIMIT, |
| 487 | + persistence_pruner_timeout: None, |
467 | 488 | } |
468 | 489 | } |
469 | 490 | } |
@@ -498,6 +519,14 @@ impl EngineArgs { |
498 | 519 | .with_sparse_trie_max_storage_tries(self.sparse_trie_max_storage_tries) |
499 | 520 | .with_disable_sparse_trie_cache_pruning(self.disable_sparse_trie_cache_pruning) |
500 | 521 | .with_state_root_task_timeout(self.state_root_task_timeout.filter(|d| !d.is_zero())) |
| 522 | + .with_persistence_pruner_delete_limit(if self.persistence_pruner_delete_limit == 0 { |
| 523 | + usize::MAX |
| 524 | + } else { |
| 525 | + self.persistence_pruner_delete_limit |
| 526 | + }) |
| 527 | + .with_persistence_pruner_timeout( |
| 528 | + self.persistence_pruner_timeout.filter(|d| !d.is_zero()), |
| 529 | + ) |
501 | 530 | } |
502 | 531 | } |
503 | 532 |
|
@@ -553,6 +582,8 @@ mod tests { |
553 | 582 | sparse_trie_max_storage_tries: 100, |
554 | 583 | disable_sparse_trie_cache_pruning: true, |
555 | 584 | state_root_task_timeout: Some(Duration::from_secs(2)), |
| 585 | + persistence_pruner_delete_limit: 100_000, |
| 586 | + persistence_pruner_timeout: Some(Duration::from_secs(30)), |
556 | 587 | }; |
557 | 588 |
|
558 | 589 | let parsed_args = CommandParser::<EngineArgs>::parse_from([ |
@@ -591,6 +622,10 @@ mod tests { |
591 | 622 | "--engine.disable-sparse-trie-cache-pruning", |
592 | 623 | "--engine.state-root-task-timeout", |
593 | 624 | "2s", |
| 625 | + "--engine.persistence-pruner-delete-limit", |
| 626 | + "100000", |
| 627 | + "--engine.persistence-pruner-timeout", |
| 628 | + "30s", |
594 | 629 | ]) |
595 | 630 | .args; |
596 | 631 |
|
|
0 commit comments