1use super::{database::parse_byte_size, DefaultRpcServerArgs};
2use clap::{builder::Resettable, Args};
3use reth_rpc_server_types::constants::cache::{
4 DEFAULT_BAL_CACHE_MAX_BYTES, DEFAULT_BAL_CACHE_MAX_LEN, DEFAULT_BLOCK_CACHE_MAX_BYTES,
5 DEFAULT_BLOCK_CACHE_MAX_LEN, DEFAULT_CACHE_IDLE_TIMEOUT, DEFAULT_CONCURRENT_DB_REQUESTS,
6 DEFAULT_MAX_CACHED_TX_HASHES, DEFAULT_RECEIPT_CACHE_MAX_BYTES, DEFAULT_RECEIPT_CACHE_MAX_LEN,
7};
8use std::time::Duration;
9
10#[derive(Debug, Clone, Args, PartialEq, Eq)]
12#[command(next_help_heading = "RPC State Cache")]
13pub struct RpcStateCacheArgs {
14 #[arg(
16 long = "rpc-cache.max-blocks",
17 default_value_t = DefaultRpcServerArgs::get_global().rpc_state_cache.max_blocks,
18 )]
19 pub max_blocks: u32,
20
21 #[arg(
23 long = "rpc-cache.max-receipts",
24 default_value_t = DefaultRpcServerArgs::get_global().rpc_state_cache.max_receipts,
25 )]
26 pub max_receipts: u32,
27
28 #[arg(
30 long = "rpc-cache.max-bals",
31 default_value_t = DefaultRpcServerArgs::get_global().rpc_state_cache.max_bals,
32 )]
33 pub max_bals: u32,
34
35 #[arg(long = "rpc-cache.max-blocks-bytes", value_name = "BYTES", value_parser = parse_byte_size, default_value_t = DefaultRpcServerArgs::get_global().rpc_state_cache.max_blocks_bytes)]
38 pub max_blocks_bytes: usize,
39
40 #[arg(long = "rpc-cache.max-receipts-bytes", value_name = "BYTES", value_parser = parse_byte_size, default_value_t = DefaultRpcServerArgs::get_global().rpc_state_cache.max_receipts_bytes)]
43 pub max_receipts_bytes: usize,
44
45 #[arg(long = "rpc-cache.max-bals-bytes", value_name = "BYTES", value_parser = parse_byte_size, default_value_t = DefaultRpcServerArgs::get_global().rpc_state_cache.max_bals_bytes)]
48 pub max_bals_bytes: usize,
49
50 #[arg(long = "rpc-cache.idle-timeout", value_name = "DURATION", value_parser = humantime::parse_duration, default_value = humantime::format_duration(DefaultRpcServerArgs::get_global().rpc_state_cache.idle_timeout).to_string())]
54 pub idle_timeout: Duration,
55
56 #[arg(long = "rpc-cache.cache-computed-bals", default_value_t = DefaultRpcServerArgs::get_global().rpc_state_cache.cache_computed_bals)]
58 pub cache_computed_bals: bool,
59
60 #[arg(
66 long = "rpc-cache.prewarm-bals",
67 value_name = "BLOCKS",
68 num_args = 0..=1,
69 default_missing_value = "0",
70 default_value = Resettable::from(DefaultRpcServerArgs::get_global().rpc_state_cache.prewarm_bals.map(|v| v.to_string().into())),
71 require_equals = true,
72 )]
73 pub prewarm_bals: Option<usize>,
74
75 #[arg(
77 long = "rpc-cache.max-concurrent-db-requests",
78 default_value_t = DefaultRpcServerArgs::get_global().rpc_state_cache.max_concurrent_db_requests,
79 )]
80 pub max_concurrent_db_requests: usize,
81
82 #[arg(
84 long = "rpc-cache.max-cached-tx-hashes",
85 default_value_t = DefaultRpcServerArgs::get_global().rpc_state_cache.max_cached_tx_hashes,
86 )]
87 pub max_cached_tx_hashes: u32,
88}
89
90impl RpcStateCacheArgs {
91 pub const fn set_zero_lengths(&mut self) {
93 self.max_blocks = 0;
94 self.max_receipts = 0;
95 self.max_bals = 0;
96 self.max_blocks_bytes = 0;
97 self.max_receipts_bytes = 0;
98 self.max_bals_bytes = 0;
99 self.idle_timeout = Duration::ZERO;
100 self.cache_computed_bals = false;
101 self.prewarm_bals = None;
102 }
103}
104
105impl Default for RpcStateCacheArgs {
106 fn default() -> Self {
107 Self {
108 max_blocks: DEFAULT_BLOCK_CACHE_MAX_LEN,
109 max_receipts: DEFAULT_RECEIPT_CACHE_MAX_LEN,
110 max_bals: DEFAULT_BAL_CACHE_MAX_LEN,
111 max_blocks_bytes: DEFAULT_BLOCK_CACHE_MAX_BYTES,
112 max_receipts_bytes: DEFAULT_RECEIPT_CACHE_MAX_BYTES,
113 max_bals_bytes: DEFAULT_BAL_CACHE_MAX_BYTES,
114 idle_timeout: DEFAULT_CACHE_IDLE_TIMEOUT,
115 cache_computed_bals: false,
116 prewarm_bals: None,
117 max_concurrent_db_requests: DEFAULT_CONCURRENT_DB_REQUESTS,
118 max_cached_tx_hashes: DEFAULT_MAX_CACHED_TX_HASHES,
119 }
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use crate::node_config::NodeConfig;
127 use clap::Parser;
128
129 #[derive(Parser)]
130 struct CommandParser {
131 #[command(flatten)]
132 args: RpcStateCacheArgs,
133 }
134
135 #[test]
136 fn rpc_cache_defaults() {
137 let args = CommandParser::parse_from(["reth"]).args;
138 assert_eq!(args, RpcStateCacheArgs::default());
139 }
140
141 #[test]
142 fn rpc_cache_byte_limits_reject_invalid_values() {
143 let overflow = format!("{}0", usize::MAX);
144 let unit_overflow = format!("{}GB", usize::MAX);
145 for flag in [
146 "--rpc-cache.max-blocks-bytes",
147 "--rpc-cache.max-receipts-bytes",
148 "--rpc-cache.max-bals-bytes",
149 ] {
150 for value in ["-1", "-1GB", "1XB", "1.5GB", &overflow, &unit_overflow] {
151 assert!(
152 CommandParser::try_parse_from(["reth", &format!("{flag}={value}")]).is_err(),
153 "accepted {flag}={value}"
154 );
155 }
156 }
157 }
158
159 #[test]
160 fn rpc_cache_byte_limits_accept_units() {
161 for (value, expected) in [
162 ("0GB", 0),
163 ("1024B", 1024),
164 ("1KB", 1024),
165 ("500MB", 500 * 1024 * 1024),
166 ("1GB", 1024 * 1024 * 1024),
167 ("1gb", 1024 * 1024 * 1024),
168 (" 500 MB ", 500 * 1024 * 1024),
169 ("1TB", 1024usize.pow(4)),
170 ] {
171 let args = CommandParser::parse_from([
172 "reth",
173 "--rpc-cache.max-blocks-bytes",
174 value,
175 "--rpc-cache.max-receipts-bytes",
176 value,
177 "--rpc-cache.max-bals-bytes",
178 value,
179 ])
180 .args;
181 assert_eq!(args.max_blocks_bytes, expected);
182 assert_eq!(args.max_receipts_bytes, expected);
183 assert_eq!(args.max_bals_bytes, expected);
184 }
185 }
186
187 #[test]
188 fn disabling_rpc_cache_overrides_positive_byte_limits() {
189 let mut config = NodeConfig::default();
190 config.rpc.rpc_state_cache = RpcStateCacheArgs {
191 max_blocks_bytes: 1024,
192 max_receipts_bytes: 2048,
193 max_bals_bytes: 4096,
194 idle_timeout: Duration::from_secs(60),
195 cache_computed_bals: true,
196 prewarm_bals: Some(10),
197 ..Default::default()
198 };
199
200 let args = config.with_disabled_rpc_cache().rpc.rpc_state_cache;
201 assert_eq!(args.max_blocks, 0);
202 assert_eq!(args.max_receipts, 0);
203 assert_eq!(args.max_bals, 0);
204 assert_eq!(args.max_blocks_bytes, 0);
205 assert_eq!(args.max_receipts_bytes, 0);
206 assert_eq!(args.max_bals_bytes, 0);
207 assert_eq!(args.idle_timeout, Duration::ZERO);
208 assert!(!args.cache_computed_bals);
209 assert_eq!(args.prewarm_bals, None);
210 }
211}