diff --git a/CHANGELOG.md b/CHANGELOG.md index 503eae69..dd99a27e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - clean up html report UI, support batched `eth_sendRawTransaction` latency metrics ([#455](https://github.com/flashbots/contender/pull/455)) - added `--scenario-label` flag to deploy and spam the same scenario under different labels ([#456](https://github.com/flashbots/contender/pull/456)) - fix: generate report when `--gen-report` is passed to `spam` ([#457](https://github.com/flashbots/contender/pull/457)) +- control `SETUP_CONCURRENCY_LIMIT` with env var ([#461](https://github.com/flashbots/contender/pull/461)) ## [0.8.1](https://github.com/flashbots/contender/releases/tag/v0.8.1) - 2026-02-09 diff --git a/crates/core/CHANGELOG.md b/crates/core/CHANGELOG.md index a614e7ad..4d409ec1 100644 --- a/crates/core/CHANGELOG.md +++ b/crates/core/CHANGELOG.md @@ -8,7 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- added `scenario_label` support to apply contract name labels at the DB boundary +- added `scenario_label` support to apply contract name labels at the DB boundary ([#456](https://github.com/flashbots/contender/pull/456)) +- control `SETUP_CONCURRENCY_LIMIT` with env var ([#461](https://github.com/flashbots/contender/pull/461)) ### Breaking changes diff --git a/crates/core/src/test_scenario.rs b/crates/core/src/test_scenario.rs index 1870fb43..9417f4fb 100644 --- a/crates/core/src/test_scenario.rs +++ b/crates/core/src/test_scenario.rs @@ -45,6 +45,7 @@ use contender_bundle_provider::{ use contender_engine_provider::ControlChain; use futures::{Stream, StreamExt}; use serde_json::json; +use std::sync::LazyLock; use std::{ collections::{BTreeMap, HashMap}, pin::Pin, @@ -58,8 +59,16 @@ use tracing::{debug, info, trace, warn}; pub use alloy::transports::http::reqwest::Url; +/// Reads the SETUP_CONCURRENCY_LIMIT from the environment, defaults to 25 if not set or invalid. +fn read_setup_concurrency_limit() -> usize { + std::env::var("SETUP_CONCURRENCY_LIMIT") + .ok() + .and_then(|val| val.parse::().ok()) + .unwrap_or(25) +} + /// Maximum concurrent setup tasks (limits open HTTP connections). -const SETUP_CONCURRENCY_LIMIT: usize = 25; +pub static SETUP_CONCURRENCY_LIMIT: LazyLock = LazyLock::new(read_setup_concurrency_limit); #[derive(Clone)] pub struct PrometheusCollector { @@ -681,7 +690,7 @@ where let genesis_hash = self.ctx.genesis_hash; let blob_base_fee = get_blob_fee_maybe(&self.rpc_client, self.tx_type).await; - let semaphore = Arc::new(tokio::sync::Semaphore::new(SETUP_CONCURRENCY_LIMIT)); + let semaphore = Arc::new(tokio::sync::Semaphore::new(*SETUP_CONCURRENCY_LIMIT)); let (_txs, updated_nonces) = self .load_txs(PlanType::Setup(|tx_req| { @@ -1917,7 +1926,7 @@ pub mod tests { }; use crate::spammer::util::test::get_test_signers; use crate::spammer::{BlockwiseSpammer, NilCallback, Spammer}; - use crate::test_scenario::TestScenario; + use crate::test_scenario::{read_setup_concurrency_limit, TestScenario}; use crate::Error; use crate::Result; use alloy::consensus::constants::GWEI_TO_WEI; @@ -2705,7 +2714,7 @@ pub mod tests { /// Reproduces the erc20-like pattern where many steps share one sender. #[tokio::test] async fn run_setup_sequential_nonces_single_sender() { - let num_steps = SETUP_CONCURRENCY_LIMIT * 3; + let num_steps = *SETUP_CONCURRENCY_LIMIT * 3; let (_anvil, mut scenario) = setup_scenario_with_anvil( SequentialNonceMockConfig { num_steps }, AgentSpec::default(), @@ -2735,8 +2744,9 @@ pub mod tests { let num_setup_steps = HighConcurrencyMockConfig.get_setup_steps().unwrap().len(); let expected_tasks = num_setup_steps * num_setup_accounts; assert!( - expected_tasks > SETUP_CONCURRENCY_LIMIT, - "test should generate more tasks ({expected_tasks}) than the concurrency limit ({SETUP_CONCURRENCY_LIMIT})" + expected_tasks > *SETUP_CONCURRENCY_LIMIT, + "test should generate more tasks ({expected_tasks}) than the concurrency limit ({})", + *SETUP_CONCURRENCY_LIMIT ); let result = scenario.run_setup().await; @@ -2746,4 +2756,18 @@ pub mod tests { result.err() ); } + + #[test] + fn env_controls_setup_concurrency_limit() { + std::env::set_var("SETUP_CONCURRENCY_LIMIT", "5"); + let new_limit = read_setup_concurrency_limit(); + assert_eq!(new_limit, 5, "SETUP_CONCURRENCY_LIMIT should be set to 5"); + + std::env::remove_var("SETUP_CONCURRENCY_LIMIT"); + let default_limit = read_setup_concurrency_limit(); + assert_eq!( + default_limit, 25, + "SETUP_CONCURRENCY_LIMIT should default to 25 when env var is unset" + ); + } } diff --git a/docs/examples.md b/docs/examples.md index 7e391b8b..7740d748 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -68,3 +68,20 @@ args = [] ```bash contender spam ./example.toml --tps 10 -e testAddr=0x0000000000000000000000000000000000000013 ``` + +## setup concurrency + +**Setup steps** can be executed in two ways: `contender setup` with a file-based scenario, or `contender spam` with a builtin scenario. + +By default, setup steps will send up to **25 transactions**, and wait for them to land onchain before sending more. + +To change this amount, set `SETUP_CONCURRENCY_LIMIT` in your environment: + +```bash +# only send 10 txs at a time +# run the erc20 scenario with 50 accounts per agent (-a) +SETUP_CONCURRENCY_LIMIT=10 \ +contender spam --tps 50 -a 50 erc20 +``` + +The builtin `erc20` scenario creates a setup step for each account, so in this case we'd have 50 setup txs to send, and you'd see 5 batches of 10 txs landing onchain, one after another.