From db8184cf91c6bc8071949c2a3757c15ed7f9f6b1 Mon Sep 17 00:00:00 2001 From: Maksim Dimitrov Date: Wed, 12 Nov 2025 17:33:16 +0200 Subject: [PATCH 1/4] graph, store: Add option to disable store call cache Signed-off-by: Maksim Dimitrov --- docs/environment-variables.md | 4 +++ graph/src/env/mod.rs | 20 +++++++++++++ graph/src/env/store.rs | 8 ++++- store/postgres/src/chain_store.rs | 8 +++++ store/test-store/tests/postgres/chain_head.rs | 30 +++++++++++++++++++ 5 files changed, 69 insertions(+), 1 deletion(-) diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 5e5bfb32d91..526bff01e70 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -294,3 +294,7 @@ those. `GRAPH_STORE_ACCOUNT_LIKE_MIN_VERSIONS_COUNT` and `GRAPH_STORE_ACCOUNT_LIKE_MAX_UNIQUE_RATIO`. - `GRAPH_STORE_ACCOUNT_LIKE_MIN_VERSIONS_COUNT`: Sets the minimum total number of versions a table must have to be considered for account-like flagging. Expects a positive integer value. No default value. - `GRAPH_STORE_ACCOUNT_LIKE_MAX_UNIQUE_RATIO`: Sets the maximum unique entities to version ratio (e.g., 0.01 ≈ 1:100 entity-to-version ratio). +- `GRAPH_STORE_DISABLE_CALL_CACHE`: Disables the store call cache entirely. Graph node will skip writing and reading from the + call cache. The buffered block call cache will still be enabled. This option may be useful + for indexers who are running their own RPC nodes. Disabling the store call cache may have + significant performance impact. (default: false) diff --git a/graph/src/env/mod.rs b/graph/src/env/mod.rs index 91cb2355088..76e2c612565 100644 --- a/graph/src/env/mod.rs +++ b/graph/src/env/mod.rs @@ -29,6 +29,7 @@ lazy_static! { lazy_static! { pub static ref TEST_WITH_NO_REORG: Mutex = Mutex::new(false); pub static ref TEST_SQL_QUERIES_ENABLED: Mutex = Mutex::new(false); + pub static ref TEST_STORE_CALL_CACHE_DISABLED: Mutex = Mutex::new(false); } /// Panics if: @@ -444,6 +445,25 @@ impl EnvVars { let mut lock = TEST_SQL_QUERIES_ENABLED.lock().unwrap(); *lock = enable; } + + #[cfg(debug_assertions)] + pub fn store_call_cache_disabled(&self) -> bool { + if *TEST_STORE_CALL_CACHE_DISABLED.lock().unwrap() { + true + } else { + self.store.disable_call_cache + } + } + + #[cfg(not(debug_assertions))] + pub fn store_call_cache_disabled(&self) -> bool { + self.store.disable_call_cache + } + + #[cfg(debug_assertions)] + pub fn set_store_call_cache_disabled_for_tests(&self, value: bool) { + *TEST_STORE_CALL_CACHE_DISABLED.lock().unwrap() = value; + } } impl Default for EnvVars { diff --git a/graph/src/env/store.rs b/graph/src/env/store.rs index efec3d693ae..3ce4105f65b 100644 --- a/graph/src/env/store.rs +++ b/graph/src/env/store.rs @@ -149,7 +149,6 @@ pub struct EnvVarsStore { /// The number of rows to fetch from the foreign data wrapper in one go, /// this will be set as the option 'fetch_size' on all foreign servers pub fdw_fetch_size: usize, - /// Experimental feature to automatically set the account-like flag on eligible tables /// Set by the environment variable `GRAPH_STORE_ACCOUNT_LIKE_SCAN_INTERVAL_HOURS` /// If not set, the job is disabled. @@ -161,6 +160,10 @@ pub struct EnvVarsStore { /// Set by the environment variable `GRAPH_STORE_ACCOUNT_LIKE_MAX_UNIQUE_RATIO` /// Defines the maximum share of unique entities (e.g. 0.01 for a 1:100 entity-to-version ratio). pub account_like_max_unique_ratio: Option, + /// Disables the store call cache entirely. Graph node will skip writing and reading from the + /// call cache. The buffered block call cache will still be enabled. + /// Set by `GRAPH_STORE_DISABLE_CALL_CACHE`. Defaults to false. + pub disable_call_cache: bool, } // This does not print any values avoid accidentally leaking any sensitive env vars @@ -221,6 +224,7 @@ impl TryFrom for EnvVarsStore { account_like_scan_interval_hours: x.account_like_scan_interval_hours, account_like_min_versions_count: x.account_like_min_versions_count, account_like_max_unique_ratio: x.account_like_max_unique_ratio.map(|r| r.0), + disable_call_cache: x.disable_call_cache, }; if let Some(timeout) = vars.batch_timeout { if timeout < 2 * vars.batch_target_duration { @@ -326,6 +330,8 @@ pub struct InnerStore { account_like_min_versions_count: Option, #[envconfig(from = "GRAPH_STORE_ACCOUNT_LIKE_MAX_UNIQUE_RATIO")] account_like_max_unique_ratio: Option, + #[envconfig(from = "GRAPH_STORE_DISABLE_CALL_CACHE", default = "false")] + disable_call_cache: bool, } #[derive(Clone, Copy, Debug)] diff --git a/store/postgres/src/chain_store.rs b/store/postgres/src/chain_store.rs index 0d5e7a182db..cc3b6949fa8 100644 --- a/store/postgres/src/chain_store.rs +++ b/store/postgres/src/chain_store.rs @@ -3081,6 +3081,10 @@ impl EthereumCallCache for ChainStore { req: &call::Request, block: BlockPtr, ) -> Result, Error> { + if ENV_VARS.store_call_cache_disabled() { + return Ok(None); + } + let id = contract_call_id(req, &block); let conn = &mut self.pool.get_permitted().await?; let return_value = conn @@ -3171,6 +3175,10 @@ impl EthereumCallCache for ChainStore { block: BlockPtr, return_value: call::Retval, ) -> Result<(), Error> { + if ENV_VARS.store_call_cache_disabled() { + return Ok(()); + } + let return_value = match return_value { call::Retval::Value(return_value) if !return_value.is_empty() => return_value, _ => { diff --git a/store/test-store/tests/postgres/chain_head.rs b/store/test-store/tests/postgres/chain_head.rs index 98f1045de7a..c6eb10f1549 100644 --- a/store/test-store/tests/postgres/chain_head.rs +++ b/store/test-store/tests/postgres/chain_head.rs @@ -529,6 +529,36 @@ fn eth_call_cache() { }) } +#[test] +fn test_disable_call_cache() { + let chain = vec![&*GENESIS_BLOCK, &*BLOCK_ONE, &*BLOCK_TWO]; + + run_test(chain, |store, _| { + ENV_VARS.set_store_call_cache_disabled_for_tests(true); + + let logger = LOGGER.cheap_clone(); + let address = H160([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5]); + let call: [u8; 6] = [1, 2, 3, 4, 5, 6]; + let return_value: [u8; 3] = [7, 8, 9]; + + let call = call::Request::new(address, call.to_vec(), 0); + + store + .set_call( + &logger, + call.cheap_clone(), + BLOCK_ONE.block_ptr(), + call::Retval::Value(Bytes::from(return_value)), + ) + .unwrap(); + + let ret = store.get_call(&call, BLOCK_ONE.block_ptr()).unwrap(); + + assert!(ret.is_none()); + Ok(()) + }); +} + #[test] /// Tests mainly query correctness. Requires data in order not to hit early returns when no stale contracts are found. fn test_clear_stale_call_cache() { From f574b6542a756c6943b116e34884066faecdc41c Mon Sep 17 00:00:00 2001 From: Maksim Dimitrov Date: Wed, 3 Dec 2025 16:11:31 +0200 Subject: [PATCH 2/4] store: Update test to async Signed-off-by: Maksim Dimitrov --- store/test-store/tests/postgres/chain_head.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/store/test-store/tests/postgres/chain_head.rs b/store/test-store/tests/postgres/chain_head.rs index c6eb10f1549..06f4fb9e937 100644 --- a/store/test-store/tests/postgres/chain_head.rs +++ b/store/test-store/tests/postgres/chain_head.rs @@ -533,7 +533,7 @@ fn eth_call_cache() { fn test_disable_call_cache() { let chain = vec![&*GENESIS_BLOCK, &*BLOCK_ONE, &*BLOCK_TWO]; - run_test(chain, |store, _| { + run_test_async(chain, |store, _, _| async move { ENV_VARS.set_store_call_cache_disabled_for_tests(true); let logger = LOGGER.cheap_clone(); @@ -544,18 +544,23 @@ fn test_disable_call_cache() { let call = call::Request::new(address, call.to_vec(), 0); store + .cheap_clone() .set_call( &logger, call.cheap_clone(), BLOCK_ONE.block_ptr(), call::Retval::Value(Bytes::from(return_value)), ) + .await .unwrap(); - let ret = store.get_call(&call, BLOCK_ONE.block_ptr()).unwrap(); + let ret = store + .cheap_clone() + .get_call(&call, BLOCK_ONE.block_ptr()) + .await + .unwrap(); assert!(ret.is_none()); - Ok(()) }); } From 8ad016d2c26705568f9a5e4f21527ac5f9ea23d4 Mon Sep 17 00:00:00 2001 From: Maksim Dimitrov Date: Wed, 14 Jan 2026 09:11:42 +0200 Subject: [PATCH 3/4] docs: small doc touch-up Signed-off-by: Maksim Dimitrov --- docs/environment-variables.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 526bff01e70..560e5fe87a4 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -290,11 +290,15 @@ those. - `GRAPH_STORE_ACCOUNT_LIKE_SCAN_INTERVAL_HOURS`: If set, enables an experimental job that periodically scans for entity tables that may benefit from an [account-like optimization](https://thegraph.com/docs/en/indexing/tooling/graph-node/#account-like-optimisation) and marks them with an account-like flag. The value is the interval in hours at which the job - should run. The job reads data from the `info.table_stats` materialized view, which refreshes every six hours. Expects an integer value, e.g., 24. Requires also setting + should run. The job reads data from the `info.table_stats` materialized view, which refreshes every six hours. + Expects an integer value, e.g., 24. Requires also setting `GRAPH_STORE_ACCOUNT_LIKE_MIN_VERSIONS_COUNT` and `GRAPH_STORE_ACCOUNT_LIKE_MAX_UNIQUE_RATIO`. -- `GRAPH_STORE_ACCOUNT_LIKE_MIN_VERSIONS_COUNT`: Sets the minimum total number of versions a table must have to be considered for account-like flagging. Expects a positive integer value. No default value. -- `GRAPH_STORE_ACCOUNT_LIKE_MAX_UNIQUE_RATIO`: Sets the maximum unique entities to version ratio (e.g., 0.01 ≈ 1:100 entity-to-version ratio). -- `GRAPH_STORE_DISABLE_CALL_CACHE`: Disables the store call cache entirely. Graph node will skip writing and reading from the - call cache. The buffered block call cache will still be enabled. This option may be useful - for indexers who are running their own RPC nodes. Disabling the store call cache may have - significant performance impact. (default: false) +- `GRAPH_STORE_ACCOUNT_LIKE_MIN_VERSIONS_COUNT`: Sets the minimum total number of versions a table must have + to be considered for account-like flagging. Expects a positive integer value. No default value. +- `GRAPH_STORE_ACCOUNT_LIKE_MAX_UNIQUE_RATIO`: Sets the maximum unique entities to version ratio + (e.g., 0.01 ≈ 1:100 entity-to-version ratio). +- `GRAPH_STORE_DISABLE_CALL_CACHE`: Disables storing or reading `eth_call` results from the store call cache. + This option may be useful for indexers who are running their own RPC nodes. + Disabling the store call cache may significantly impact performance; the actual impact depends on + the average execution time of an `eth_call` compared to the cost of a database lookup for a cached result. + (default: false) From 29c41eb577da913a38c130594924afbb403fd6b6 Mon Sep 17 00:00:00 2001 From: Maksim Dimitrov Date: Wed, 14 Jan 2026 09:27:49 +0200 Subject: [PATCH 4/4] graph: Update inline docs Signed-off-by: Maksim Dimitrov --- graph/src/env/store.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/graph/src/env/store.rs b/graph/src/env/store.rs index 3ce4105f65b..7a3c16d5b81 100644 --- a/graph/src/env/store.rs +++ b/graph/src/env/store.rs @@ -160,8 +160,7 @@ pub struct EnvVarsStore { /// Set by the environment variable `GRAPH_STORE_ACCOUNT_LIKE_MAX_UNIQUE_RATIO` /// Defines the maximum share of unique entities (e.g. 0.01 for a 1:100 entity-to-version ratio). pub account_like_max_unique_ratio: Option, - /// Disables the store call cache entirely. Graph node will skip writing and reading from the - /// call cache. The buffered block call cache will still be enabled. + /// Disables storing or reading `eth_call` results from the store call cache. /// Set by `GRAPH_STORE_DISABLE_CALL_CACHE`. Defaults to false. pub disable_call_cache: bool, }