Skip to main content

miden_node_utils/
clap.rs

1//! Public module for share clap pieces to reduce duplication
2
3use std::num::{NonZeroU32, NonZeroU64};
4use std::time::Duration;
5
6#[cfg(feature = "rocksdb")]
7mod rocksdb;
8#[cfg(feature = "rocksdb")]
9pub use rocksdb::*;
10
11const DEFAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(10);
12const TEST_REQUEST_TIMEOUT: Duration = Duration::from_secs(5);
13const DEFAULT_MAX_CONNECTION_AGE: Duration = Duration::from_mins(30);
14const DEFAULT_MAX_CONNECTION_AGE_GRACE: Duration = Duration::from_secs(10);
15const DEFAULT_REPLENISH_N_PER_SECOND_PER_IP: NonZeroU64 = NonZeroU64::new(16).unwrap();
16const DEFAULT_BURST_SIZE: NonZeroU32 = NonZeroU32::new(128).unwrap();
17const DEFAULT_MAX_CONCURRENT_CONNECTIONS: u64 = 1_000;
18
19// Formats a Duration into a human-readable string for display in clap help text and yields a
20// &'static str by _leaking_ the string deliberately.
21pub fn duration_to_human_readable_string(duration: Duration) -> &'static str {
22    Box::new(humantime::format_duration(duration).to_string()).leak()
23}
24
25#[derive(clap::Args, Copy, Clone, Debug, PartialEq, Eq)]
26pub struct GrpcOptionsInternal {
27    /// Maximum duration a gRPC request is allocated before being dropped by the server.
28    ///
29    /// This may occur if the server is overloaded or due to an internal bug.
30    #[arg(
31        long = "grpc.timeout",
32        default_value = duration_to_human_readable_string(DEFAULT_REQUEST_TIMEOUT),
33        value_parser = humantime::parse_duration,
34        value_name = "DURATION"
35    )]
36    pub request_timeout: Duration,
37}
38
39impl Default for GrpcOptionsInternal {
40    fn default() -> Self {
41        Self { request_timeout: DEFAULT_REQUEST_TIMEOUT }
42    }
43}
44
45impl From<GrpcOptionsExternal> for GrpcOptionsInternal {
46    fn from(value: GrpcOptionsExternal) -> Self {
47        let GrpcOptionsExternal { request_timeout, .. } = value;
48        Self { request_timeout }
49    }
50}
51
52impl GrpcOptionsInternal {
53    pub fn test() -> Self {
54        GrpcOptionsExternal::test().into()
55    }
56    pub fn bench() -> Self {
57        GrpcOptionsExternal::bench().into()
58    }
59}
60
61#[derive(clap::Args, Copy, Clone, Debug, PartialEq, Eq)]
62pub struct GrpcOptionsExternal {
63    /// Maximum duration a gRPC request is allocated before being dropped by the server.
64    ///
65    /// This may occur if the server is overloaded or due to an internal bug.
66    #[arg(
67        long = "grpc.timeout",
68        default_value = duration_to_human_readable_string(DEFAULT_REQUEST_TIMEOUT),
69        value_parser = humantime::parse_duration,
70        value_name = "DURATION"
71    )]
72    pub request_timeout: Duration,
73
74    /// Maximum duration of a connection before we drop it on the server side irrespective of
75    /// activity.
76    #[arg(
77        long = "grpc.max_connection_age",
78        default_value = duration_to_human_readable_string(DEFAULT_MAX_CONNECTION_AGE),
79        value_parser = humantime::parse_duration,
80        value_name = "MAX_CONNECTION_AGE"
81    )]
82    pub max_connection_age: Duration,
83
84    /// Maximum duration for a connection to shut down gracefully after reaching the maximum age
85    /// before the server forcefully closes it.
86    #[arg(
87        long = "grpc.max_connection_age_grace",
88        default_value = duration_to_human_readable_string(DEFAULT_MAX_CONNECTION_AGE_GRACE),
89        value_parser = humantime::parse_duration,
90        value_name = "MAX_CONNECTION_AGE_GRACE"
91    )]
92    pub max_connection_age_grace: Duration,
93
94    /// Number of connections to be served before the "API tokens" need to be replenished per IP
95    /// address.
96    #[arg(
97        long = "grpc.burst_size",
98        default_value_t = DEFAULT_BURST_SIZE,
99        value_name = "BURST_SIZE"
100    )]
101    pub burst_size: NonZeroU32,
102
103    /// Number of request credits replenished per second per IP.
104    #[arg(
105        long = "grpc.replenish_n_per_second",
106        default_value_t = DEFAULT_REPLENISH_N_PER_SECOND_PER_IP,
107        value_name = "DEFAULT_REPLENISH_N_PER_SECOND"
108    )]
109    pub replenish_n_per_second_per_ip: NonZeroU64,
110
111    /// Maximum number of concurrent connections accepted by the server.
112    #[arg(
113        long = "grpc.max_concurrent_connections",
114        default_value_t = DEFAULT_MAX_CONCURRENT_CONNECTIONS,
115        value_name = "MAX_CONCURRENT_CONNECTIONS"
116    )]
117    pub max_concurrent_connections: u64,
118}
119
120impl Default for GrpcOptionsExternal {
121    fn default() -> Self {
122        Self {
123            request_timeout: DEFAULT_REQUEST_TIMEOUT,
124            max_connection_age: DEFAULT_MAX_CONNECTION_AGE,
125            max_connection_age_grace: DEFAULT_MAX_CONNECTION_AGE_GRACE,
126            burst_size: DEFAULT_BURST_SIZE,
127            replenish_n_per_second_per_ip: DEFAULT_REPLENISH_N_PER_SECOND_PER_IP,
128            max_concurrent_connections: DEFAULT_MAX_CONCURRENT_CONNECTIONS,
129        }
130    }
131}
132
133impl GrpcOptionsExternal {
134    pub fn test() -> Self {
135        Self {
136            request_timeout: TEST_REQUEST_TIMEOUT,
137            ..Default::default()
138        }
139    }
140
141    /// Return a gRPC config for benchmarking.
142    pub fn bench() -> Self {
143        Self {
144            request_timeout: Duration::from_hours(24),
145            max_connection_age: Duration::from_hours(24),
146            max_connection_age_grace: DEFAULT_MAX_CONNECTION_AGE_GRACE,
147            burst_size: NonZeroU32::new(100_000).unwrap(),
148            replenish_n_per_second_per_ip: NonZeroU64::new(100_000).unwrap(),
149            max_concurrent_connections: u64::MAX,
150        }
151    }
152}
153
154/// Collection of per usage storage backend configurations, plus store write-path tuning.
155#[derive(clap::Args, Clone, Debug, PartialEq, Eq)]
156pub struct StorageOptions {
157    #[cfg(feature = "rocksdb")]
158    #[clap(flatten)]
159    pub account_tree: AccountTreeRocksDbOptions,
160    #[cfg(feature = "rocksdb")]
161    #[clap(flatten)]
162    pub nullifier_tree: NullifierTreeRocksDbOptions,
163    #[cfg(feature = "rocksdb")]
164    #[clap(flatten)]
165    pub account_state_forest: AccountStateForestRocksDbOptions,
166
167    /// Whether the store's apply-block thread pool runs at raised OS thread priority (best-effort).
168    #[arg(
169        id = "apply_block_thread_priority",
170        long = "apply_block.thread_priority",
171        default_value_t = true,
172        action = clap::ArgAction::Set,
173        value_name = "BOOL"
174    )]
175    pub apply_block_thread_priority: bool,
176}
177
178impl Default for StorageOptions {
179    fn default() -> Self {
180        Self {
181            #[cfg(feature = "rocksdb")]
182            account_tree: AccountTreeRocksDbOptions::default(),
183            #[cfg(feature = "rocksdb")]
184            nullifier_tree: NullifierTreeRocksDbOptions::default(),
185            #[cfg(feature = "rocksdb")]
186            account_state_forest: AccountStateForestRocksDbOptions::default(),
187            apply_block_thread_priority: true,
188        }
189    }
190}
191
192impl StorageOptions {
193    /// Benchmark setup.
194    ///
195    /// These values were determined during development of `LargeSmt`
196    pub fn bench() -> Self {
197        #[cfg(feature = "rocksdb")]
198        {
199            let account_tree = AccountTreeRocksDbOptions {
200                max_open_fds: self::rocksdb::BENCH_ROCKSDB_MAX_OPEN_FDS,
201                cache_size_in_bytes: self::rocksdb::DEFAULT_ROCKSDB_CACHE_SIZE,
202                durability_mode: None,
203            };
204            let nullifier_tree = NullifierTreeRocksDbOptions {
205                max_open_fds: BENCH_ROCKSDB_MAX_OPEN_FDS,
206                cache_size_in_bytes: DEFAULT_ROCKSDB_CACHE_SIZE,
207                durability_mode: None,
208            };
209            let account_state_forest = AccountStateForestRocksDbOptions {
210                max_open_fds: BENCH_ROCKSDB_MAX_OPEN_FDS,
211                cache_size_in_bytes: DEFAULT_ROCKSDB_CACHE_SIZE,
212                durability_mode: None,
213            };
214            Self {
215                account_tree,
216                nullifier_tree,
217                account_state_forest,
218                apply_block_thread_priority: true,
219            }
220        }
221        #[cfg(not(feature = "rocksdb"))]
222        Self::default()
223    }
224}