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