Skip to content

Commit 0c9940a

Browse files
Per chain rpc settings (#6459)
* ethereum, node: Per-chain RPC settings via TOML config Introduce a ChainSettings struct that holds Ethereum RPC tuning parameters (timeouts, retries, batch sizes, block ranges, etc.) per chain instead of reading them from global ENV_VARS. Settings are parsed from [chains.<name>] TOML sections with serde defaults falling back to the existing environment variables, preserving full backwards compatibility. Thread ChainSettings through Chain, EthereumAdapter, and the free receipt-fetching functions so each chain can be independently tuned. * docs: Document per-chain RPC settings in config and env vars Add the full list of per-chain Ethereum RPC tuning settings to docs/config.md with env var fallback defaults, and add a cross-reference note in docs/environment-variables.md. * node: Validate per-chain settings on config load Reject invalid ChainSettings values at startup: max_block_range_size and max_event_only_range must be positive (i32), and block_batch_size, block_ptr_batch_size, block_ingestor_max_concurrent_json_rpc_calls, and get_logs_max_contracts must be non-zero (used as .buffered() args or filter batch limits where 0 would panic or silently break). * node, docs: Address review feedback on per-chain settings - Guard ChainSettings::validate() to Ethereum chains only - Add zero-value validation for polling_interval, json_rpc_timeout, request_retries, and target_triggers_per_block_range - Fix ETHEREUM_POLLING_INTERVAL env var name in doc comment (drop GRAPH_ prefix) - Fix polling_interval default in docs (500ms → 1000ms)
1 parent 1d39f7d commit 0c9940a

File tree

12 files changed

+765
-100
lines changed

12 files changed

+765
-100
lines changed

chain/ethereum/src/adapter.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ pub struct EthereumLogFilter {
364364

365365
impl From<EthereumLogFilter> for Vec<LogFilter> {
366366
fn from(val: EthereumLogFilter) -> Self {
367-
val.eth_get_logs_filters()
367+
val.eth_get_logs_filters(ENV_VARS.get_logs_max_contracts)
368368
.map(
369369
|EthGetLogsFilter {
370370
contracts,
@@ -546,7 +546,10 @@ impl EthereumLogFilter {
546546
/// Filters for `eth_getLogs` calls. The filters will not return false positives. This attempts
547547
/// to balance between having granular filters but too many calls and having few calls but too
548548
/// broad filters causing the Ethereum endpoint to timeout.
549-
pub fn eth_get_logs_filters(self) -> impl Iterator<Item = EthGetLogsFilter> {
549+
pub fn eth_get_logs_filters(
550+
self,
551+
get_logs_max_contracts: usize,
552+
) -> impl Iterator<Item = EthGetLogsFilter> {
550553
let mut filters = Vec::new();
551554

552555
// Start with the wildcard event filters.
@@ -596,7 +599,7 @@ impl EthereumLogFilter {
596599
for neighbor in g.neighbors(max_vertex) {
597600
match neighbor {
598601
LogFilterNode::Contract(address) => {
599-
if filter.contracts.len() == ENV_VARS.get_logs_max_contracts {
602+
if filter.contracts.len() == get_logs_max_contracts {
600603
// The batch size was reached, register the filter and start a new one.
601604
let event = filter.event_signatures[0];
602605
push_filter(filter);
@@ -1771,7 +1774,7 @@ fn complete_log_filter() {
17711774
wildcard_events: HashMap::new(),
17721775
events_with_topic_filters: HashMap::new(),
17731776
}
1774-
.eth_get_logs_filters()
1777+
.eth_get_logs_filters(ENV_VARS.get_logs_max_contracts)
17751778
.collect();
17761779

17771780
// Assert that a contract or event is filtered on iff it was present in the graph.

chain/ethereum/src/chain.rs

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,44 @@ use graph::blockchain::block_stream::{
6868
/// Celo Mainnet: 42220, Testnet Alfajores: 44787, Testnet Baklava: 62320
6969
const CELO_CHAIN_IDS: [u64; 3] = [42220, 44787, 62320];
7070

71+
/// Resolved per-chain settings. Populated at chain initialisation from the config file (with
72+
/// ENV_VAR fallbacks) and stored on [`Chain`] and [`crate::EthereumAdapter`].
73+
#[derive(Clone, Debug)]
74+
pub struct ChainSettings {
75+
pub polling_interval: Duration,
76+
pub json_rpc_timeout: Duration,
77+
pub request_retries: usize,
78+
pub max_block_range_size: BlockNumber,
79+
pub block_batch_size: usize,
80+
pub block_ptr_batch_size: usize,
81+
pub max_event_only_range: BlockNumber,
82+
pub target_triggers_per_block_range: u64,
83+
pub get_logs_max_contracts: usize,
84+
pub block_ingestor_max_concurrent_json_rpc_calls: usize,
85+
pub genesis_block_number: u64,
86+
}
87+
88+
impl ChainSettings {
89+
/// Constructs a [`ChainSettings`] from environment variable defaults.
90+
/// Used in tests and for firehose-only chains that have no RPC config.
91+
pub fn from_env_defaults() -> Self {
92+
ChainSettings {
93+
polling_interval: graph::env::ENV_VARS.ingestor_polling_interval,
94+
json_rpc_timeout: ENV_VARS.json_rpc_timeout,
95+
request_retries: ENV_VARS.request_retries,
96+
max_block_range_size: ENV_VARS.max_block_range_size,
97+
block_batch_size: ENV_VARS.block_batch_size,
98+
block_ptr_batch_size: ENV_VARS.block_ptr_batch_size,
99+
max_event_only_range: ENV_VARS.max_event_only_range,
100+
target_triggers_per_block_range: ENV_VARS.target_triggers_per_block_range,
101+
get_logs_max_contracts: ENV_VARS.get_logs_max_contracts,
102+
block_ingestor_max_concurrent_json_rpc_calls: ENV_VARS
103+
.block_ingestor_max_concurrent_json_rpc_calls,
104+
genesis_block_number: ENV_VARS.genesis_block_number,
105+
}
106+
}
107+
}
108+
71109
pub struct EthereumStreamBuilder {}
72110

73111
#[async_trait]
@@ -192,9 +230,9 @@ impl BlockStreamBuilder<Chain> for EthereumStreamBuilder {
192230
};
193231

194232
let max_block_range_size = if is_using_subgraph_composition {
195-
ENV_VARS.max_block_range_size * 10
233+
chain.settings.max_block_range_size * 10
196234
} else {
197-
ENV_VARS.max_block_range_size
235+
chain.settings.max_block_range_size
198236
};
199237

200238
Ok(Box::new(PollingBlockStream::new(
@@ -206,7 +244,7 @@ impl BlockStreamBuilder<Chain> for EthereumStreamBuilder {
206244
reorg_threshold,
207245
logger,
208246
max_block_range_size,
209-
ENV_VARS.target_triggers_per_block_range,
247+
chain.settings.target_triggers_per_block_range,
210248
unified_api_version,
211249
subgraph_current_block,
212250
)))
@@ -325,13 +363,13 @@ pub struct Chain {
325363
call_cache: Arc<dyn EthereumCallCache>,
326364
chain_head_update_listener: Arc<dyn ChainHeadUpdateListener>,
327365
reorg_threshold: BlockNumber,
328-
polling_ingestor_interval: Duration,
329366
pub is_ingestible: bool,
330367
block_stream_builder: Arc<dyn BlockStreamBuilder<Self>>,
331368
block_refetcher: Arc<dyn BlockRefetcher<Self>>,
332369
adapter_selector: Arc<dyn TriggersAdapterSelector<Self>>,
333370
runtime_adapter_builder: Arc<dyn RuntimeAdapterBuilder>,
334371
eth_adapters: Arc<EthereumNetworkAdapters>,
372+
pub settings: Arc<ChainSettings>,
335373
}
336374

337375
impl std::fmt::Debug for Chain {
@@ -388,8 +426,8 @@ impl Chain {
388426
runtime_adapter_builder: Arc<dyn RuntimeAdapterBuilder>,
389427
eth_adapters: Arc<EthereumNetworkAdapters>,
390428
reorg_threshold: BlockNumber,
391-
polling_ingestor_interval: Duration,
392429
is_ingestible: bool,
430+
settings: Arc<ChainSettings>,
393431
) -> Self {
394432
Chain {
395433
logger_factory,
@@ -406,7 +444,7 @@ impl Chain {
406444
eth_adapters,
407445
reorg_threshold,
408446
is_ingestible,
409-
polling_ingestor_interval,
447+
settings,
410448
}
411449
}
412450

@@ -637,7 +675,7 @@ impl Blockchain for Chain {
637675
graph::env::ENV_VARS.reorg_threshold(),
638676
self.chain_client(),
639677
self.chain_store.cheap_clone(),
640-
self.polling_ingestor_interval,
678+
self.settings.polling_interval,
641679
self.name.clone(),
642680
)?)
643681
}

0 commit comments

Comments
 (0)