Skip to content

Commit f966065

Browse files
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 cc3a6ec commit f966065

3 files changed

Lines changed: 104 additions & 9 deletions

File tree

docs/config.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ default (see [environment-variables.md](environment-variables.md) for
136136
details):
137137

138138
- `polling_interval`: block ingestor polling interval in milliseconds.
139-
Default: `ETHEREUM_POLLING_INTERVAL` (500ms).
139+
Default: `ETHEREUM_POLLING_INTERVAL` (1000ms).
140140
- `json_rpc_timeout`: timeout for JSON-RPC requests in seconds.
141141
Default: `GRAPH_ETHEREUM_JSON_RPC_TIMEOUT` (180s).
142142
- `request_retries`: number of times to retry failed JSON-RPC requests.

docs/environment-variables.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ those.
1818
- `ETHEREUM_REORG_THRESHOLD`: Maximum expected reorg size, if a larger reorg
1919
happens, subgraphs might process inconsistent data. Defaults to 250.
2020
- `ETHEREUM_POLLING_INTERVAL`: how often to poll Ethereum for new blocks (in ms,
21-
defaults to 500ms)
21+
defaults to 1000ms)
2222
- `GRAPH_ETHEREUM_TARGET_TRIGGERS_PER_BLOCK_RANGE`: The ideal amount of triggers
2323
to be processed in a batch. If this is too small it may cause too many requests
2424
to the ethereum node, if it is too large it may cause unreasonably expensive

node/src/config.rs

Lines changed: 102 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,9 @@ impl ChainSection {
489489
}
490490

491491
for (name, chain) in self.chains.iter_mut() {
492-
chain.validate(name)?;
492+
chain
493+
.validate()
494+
.map_err(|e| anyhow!("chain '{}': {}", name, e))?;
493495
if chain.cache_size <= reorg_threshold {
494496
return Err(anyhow!(
495497
"chain '{}': cache_size ({}) must be greater than reorg_threshold ({})",
@@ -641,7 +643,7 @@ impl ChainSection {
641643
/// `polling_interval`.
642644
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
643645
pub struct ChainSettings {
644-
/// Set by `polling_interval` (milliseconds). Defaults to `GRAPH_ETHEREUM_POLLING_INTERVAL`.
646+
/// Set by `polling_interval` (milliseconds). Defaults to `ETHEREUM_POLLING_INTERVAL`.
645647
#[serde(
646648
default = "default_polling_interval",
647649
deserialize_with = "deserialize_duration_millis"
@@ -703,6 +705,15 @@ impl Default for ChainSettings {
703705

704706
impl ChainSettings {
705707
fn validate(&self) -> Result<()> {
708+
anyhow::ensure!(
709+
!self.polling_interval.is_zero(),
710+
"polling_interval must be > 0"
711+
);
712+
anyhow::ensure!(
713+
!self.json_rpc_timeout.is_zero(),
714+
"json_rpc_timeout must be > 0"
715+
);
716+
anyhow::ensure!(self.request_retries > 0, "request_retries must be > 0");
706717
anyhow::ensure!(
707718
self.max_block_range_size > 0,
708719
"max_block_range_size must be > 0"
@@ -724,6 +735,10 @@ impl ChainSettings {
724735
self.get_logs_max_contracts > 0,
725736
"get_logs_max_contracts must be > 0"
726737
);
738+
anyhow::ensure!(
739+
self.target_triggers_per_block_range > 0,
740+
"target_triggers_per_block_range must be > 0"
741+
);
727742
Ok(())
728743
}
729744
}
@@ -758,22 +773,22 @@ fn default_blockchain_kind() -> BlockchainKind {
758773
}
759774

760775
impl Chain {
761-
fn validate(&mut self, name: &str) -> Result<()> {
776+
fn validate(&mut self) -> Result<()> {
762777
let mut labels = self.providers.iter().map(|p| &p.label).collect_vec();
763778
labels.sort();
764779
labels.dedup();
765780
if labels.len() != self.providers.len() {
766-
return Err(anyhow!("chain {}: provider labels must be unique", name));
781+
return Err(anyhow!("provider labels must be unique"));
767782
}
768783

769784
// `Config` validates that `self.shard` references a configured shard
770785
for provider in self.providers.iter_mut() {
771786
provider.validate()?
772787
}
773788

774-
self.settings
775-
.validate()
776-
.map_err(|e| anyhow!("chain {}: {}", name, e))?;
789+
if self.protocol == BlockchainKind::Ethereum {
790+
self.settings.validate()?;
791+
}
777792

778793
Ok(())
779794
}
@@ -2594,6 +2609,86 @@ fdw_pool_size = [
25942609
);
25952610
}
25962611

2612+
#[test]
2613+
fn chain_settings_rejects_zero_polling_interval() {
2614+
let mut section = toml::from_str::<ChainSection>(
2615+
r#"
2616+
ingestor = "block_ingestor_node"
2617+
[mainnet]
2618+
shard = "primary"
2619+
polling_interval = 0
2620+
provider = []
2621+
"#,
2622+
)
2623+
.unwrap();
2624+
2625+
let err = section.validate().unwrap_err().to_string();
2626+
assert!(
2627+
err.contains("polling_interval must be > 0"),
2628+
"expected polling_interval error, got: {err}"
2629+
);
2630+
}
2631+
2632+
#[test]
2633+
fn chain_settings_rejects_zero_json_rpc_timeout() {
2634+
let mut section = toml::from_str::<ChainSection>(
2635+
r#"
2636+
ingestor = "block_ingestor_node"
2637+
[mainnet]
2638+
shard = "primary"
2639+
json_rpc_timeout = 0
2640+
provider = []
2641+
"#,
2642+
)
2643+
.unwrap();
2644+
2645+
let err = section.validate().unwrap_err().to_string();
2646+
assert!(
2647+
err.contains("json_rpc_timeout must be > 0"),
2648+
"expected json_rpc_timeout error, got: {err}"
2649+
);
2650+
}
2651+
2652+
#[test]
2653+
fn chain_settings_rejects_zero_request_retries() {
2654+
let mut section = toml::from_str::<ChainSection>(
2655+
r#"
2656+
ingestor = "block_ingestor_node"
2657+
[mainnet]
2658+
shard = "primary"
2659+
request_retries = 0
2660+
provider = []
2661+
"#,
2662+
)
2663+
.unwrap();
2664+
2665+
let err = section.validate().unwrap_err().to_string();
2666+
assert!(
2667+
err.contains("request_retries must be > 0"),
2668+
"expected request_retries error, got: {err}"
2669+
);
2670+
}
2671+
2672+
#[test]
2673+
fn chain_settings_rejects_zero_target_triggers_per_block_range() {
2674+
let mut section = toml::from_str::<ChainSection>(
2675+
r#"
2676+
ingestor = "block_ingestor_node"
2677+
[mainnet]
2678+
shard = "primary"
2679+
target_triggers_per_block_range = 0
2680+
provider = []
2681+
"#,
2682+
)
2683+
.unwrap();
2684+
2685+
let err = section.validate().unwrap_err().to_string();
2686+
assert!(
2687+
err.contains("target_triggers_per_block_range must be > 0"),
2688+
"expected target_triggers_per_block_range error, got: {err}"
2689+
);
2690+
}
2691+
25972692
#[test]
25982693
fn chain_settings_validation_error_includes_chain_name() {
25992694
let mut section = toml::from_str::<ChainSection>(

0 commit comments

Comments
 (0)