Skip to content

Commit 5607794

Browse files
authored
Merge pull request #9 from ithacaxyz/tim/rate-limit
feature: add optional rate-limit
2 parents b45d977 + b6e44e8 commit 5607794

File tree

3 files changed

+51
-1
lines changed

3 files changed

+51
-1
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,7 @@ Options:
1212
--use-reth Whether to query reth namespace
1313
--use-tracing Whether to query tracing methods
1414
--use-all-txes Whether to query every transacion from a block or just the first
15+
--rate-limit <RATE_LIMIT> Maximum requests per second (rate limit)
16+
--timeout <TIMEOUT> Maximum time to wait for syncing in seconds [default: 300]
1517
-h, --help Print help
1618
```

crates/rpc-tester-cli/src/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ pub struct CliArgs {
4343
/// Maximum time to wait for syncing in seconds
4444
#[arg(long, value_name = "TIMEOUT", default_value = "300")]
4545
pub timeout: u64,
46+
47+
/// Maximum requests per second (rate limit).
48+
/// If not provided, no rate limiting is applied.
49+
#[arg(long, value_name = "RATE_LIMIT")]
50+
pub rate_limit: Option<u32>,
4651
}
4752

4853
#[tokio::main]
@@ -66,6 +71,7 @@ async fn main() -> eyre::Result<()> {
6671
.with_tracing(args.use_tracing)
6772
.with_reth(args.use_reth)
6873
.with_all_txes(args.use_all_txes)
74+
.with_rate_limit(args.rate_limit)
6975
.build()
7076
.run(block_range)
7177
.await

crates/rpc-tester/src/tester.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ pub struct RpcTester<P: Provider<AnyNetwork>> {
4444
/// Whether to call rpc transaction methods for every transaction. Otherwise, just the first of
4545
/// the block.
4646
use_all_txes: bool,
47+
/// Maximum requests per second for rate limiting.
48+
rate_limit_rps: Option<u32>,
49+
/// Last timestamp for rate limiting.
50+
last_request_time: tokio::sync::Mutex<std::time::Instant>,
4751
}
4852

4953
impl<P: Provider<AnyNetwork>> RpcTester<P> {
@@ -196,6 +200,23 @@ where
196200
Ok((block, block_hash, block_tag, block_id))
197201
}
198202

203+
/// Apply rate limiting if configured.
204+
/// Sleeps if necessary to maintain the configured rate limit.
205+
async fn apply_rate_limit(&self) {
206+
if let Some(rps) = self.rate_limit_rps {
207+
let min_interval = std::time::Duration::from_secs_f64(1.0 / rps as f64);
208+
let mut last_time = self.last_request_time.lock().await;
209+
let now = std::time::Instant::now();
210+
let elapsed = now.duration_since(*last_time);
211+
if elapsed < min_interval {
212+
let sleep_time = min_interval - elapsed;
213+
debug!("Rate limiting: sleeping for {:?}", sleep_time);
214+
tokio::time::sleep(sleep_time).await;
215+
}
216+
*last_time = std::time::Instant::now();
217+
}
218+
}
219+
199220
/// Compares the response to a specific method between both rpcs. Only collects differences.
200221
///
201222
/// If any namespace is disabled skip it.
@@ -215,6 +236,9 @@ where
215236
return (name.to_string(), Ok(()));
216237
}
217238

239+
// Apply rate limiting if configured
240+
self.apply_rate_limit().await;
241+
218242
trace!("## {name}");
219243
let t = std::time::Instant::now();
220244
let (rpc1_result, rpc2_result) =
@@ -254,12 +278,21 @@ pub struct RpcTesterBuilder<P: Provider<AnyNetwork>> {
254278
/// Whether to call rpc transaction methods for every transaction. Otherwise, just the first of
255279
/// the block.
256280
use_all_txes: bool,
281+
/// Maximum requests per second for rate limiting.
282+
rate_limit_rps: Option<u32>,
257283
}
258284

259285
impl<P: Provider<AnyNetwork>> RpcTesterBuilder<P> {
260286
/// Creates a new builder with default settings.
261287
pub const fn new(rpc1: P, rpc2: P) -> Self {
262-
Self { rpc1, rpc2, use_tracing: false, use_reth: false, use_all_txes: false }
288+
Self {
289+
rpc1,
290+
rpc2,
291+
use_tracing: false,
292+
use_reth: false,
293+
use_all_txes: false,
294+
rate_limit_rps: None,
295+
}
263296
}
264297

265298
/// Enables or disables tracing calls.
@@ -281,6 +314,13 @@ impl<P: Provider<AnyNetwork>> RpcTesterBuilder<P> {
281314
self
282315
}
283316

317+
/// Sets the rate limit in requests per second.
318+
/// If None, no rate limiting is applied.
319+
pub const fn with_rate_limit(mut self, rps: Option<u32>) -> Self {
320+
self.rate_limit_rps = rps;
321+
self
322+
}
323+
284324
/// Builds and returns the [`RpcTester`].
285325
pub fn build(self) -> RpcTester<P> {
286326
RpcTester {
@@ -289,6 +329,8 @@ impl<P: Provider<AnyNetwork>> RpcTesterBuilder<P> {
289329
use_tracing: self.use_tracing,
290330
use_reth: self.use_reth,
291331
use_all_txes: self.use_all_txes,
332+
rate_limit_rps: self.rate_limit_rps,
333+
last_request_time: tokio::sync::Mutex::new(std::time::Instant::now()),
292334
}
293335
}
294336
}

0 commit comments

Comments
 (0)