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.
155///
156/// Note: Currently only contains `rocksdb` related configuration.
157#[derive(clap::Args, Clone, Debug, Default, PartialEq, Eq)]
158pub struct StorageOptions {
159    #[cfg(feature = "rocksdb")]
160    #[clap(flatten)]
161    pub account_tree: AccountTreeRocksDbOptions,
162    #[cfg(feature = "rocksdb")]
163    #[clap(flatten)]
164    pub nullifier_tree: NullifierTreeRocksDbOptions,
165    #[cfg(feature = "rocksdb")]
166    #[clap(flatten)]
167    pub account_state_forest: AccountStateForestRocksDbOptions,
168}
169
170impl StorageOptions {
171    /// Benchmark setup.
172    ///
173    /// These values were determined during development of `LargeSmt`
174    pub fn bench() -> Self {
175        #[cfg(feature = "rocksdb")]
176        {
177            let account_tree = AccountTreeRocksDbOptions {
178                max_open_fds: self::rocksdb::BENCH_ROCKSDB_MAX_OPEN_FDS,
179                cache_size_in_bytes: self::rocksdb::DEFAULT_ROCKSDB_CACHE_SIZE,
180                durability_mode: None,
181            };
182            let nullifier_tree = NullifierTreeRocksDbOptions {
183                max_open_fds: BENCH_ROCKSDB_MAX_OPEN_FDS,
184                cache_size_in_bytes: DEFAULT_ROCKSDB_CACHE_SIZE,
185                durability_mode: None,
186            };
187            let account_state_forest = AccountStateForestRocksDbOptions {
188                max_open_fds: BENCH_ROCKSDB_MAX_OPEN_FDS,
189                cache_size_in_bytes: DEFAULT_ROCKSDB_CACHE_SIZE,
190                durability_mode: None,
191            };
192            Self {
193                account_tree,
194                nullifier_tree,
195                account_state_forest,
196            }
197        }
198        #[cfg(not(feature = "rocksdb"))]
199        Self::default()
200    }
201}