starfish_config/
parameters.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{path::PathBuf, time::Duration};
6
7use serde::{Deserialize, Serialize};
8
9/// Operational configurations of a consensus authority.
10///
11/// All fields should tolerate inconsistencies among authorities, without
12/// affecting safety of the protocol. Otherwise, they need to be part of IOTA
13/// protocol config or epoch state on-chain.
14///
15/// NOTE: fields with default values are specified in the serde default
16/// functions. Most operators should not need to specify any field, except
17/// db_path.
18#[derive(Clone, Debug, Deserialize, Serialize)]
19pub struct Parameters {
20    /// Path to consensus DB for this epoch. Required when initializing
21    /// consensus. This is calculated based on user configuration for base
22    /// directory.
23    #[serde(skip)]
24    pub db_path: PathBuf,
25
26    /// Time to wait for parent round leader before sealing a block, from when
27    /// parent round has a quorum.
28    #[serde(default = "Parameters::default_leader_timeout")]
29    pub leader_timeout: Duration,
30
31    /// Minimum delay between own blocks. This avoids generating too many rounds
32    /// when latency is low. This is especially necessary for tests running
33    /// locally. If setting a non-default value, it should be set low enough
34    /// to avoid reducing round rate and increasing latency in realistic and
35    /// distributed configurations.
36    #[serde(default = "Parameters::default_min_block_delay")]
37    pub min_block_delay: Duration,
38
39    /// Maximum forward time drift (how far in future) allowed for received
40    /// blocks.
41    #[serde(default = "Parameters::default_max_forward_time_drift")]
42    pub max_forward_time_drift: Duration,
43
44    /// Number of block headers to fetch per commit sync request.
45    #[serde(default = "Parameters::default_max_headers_per_commit_sync_fetch")]
46    pub max_headers_per_commit_sync_fetch: usize,
47
48    /// Number of transactions to fetch per commit sync request.
49    #[serde(default = "Parameters::default_max_transactions_per_commit_sync_fetch")]
50    pub max_transactions_per_commit_sync_fetch: usize,
51
52    /// Number of block headers to fetch per periodic or live sync request
53    #[serde(default = "Parameters::default_max_headers_per_regular_sync_fetch")]
54    pub max_headers_per_regular_sync_fetch: usize,
55
56    /// Number of transactions to fetch per request.
57    #[serde(default = "Parameters::default_max_transactions_per_regular_sync_fetch")]
58    pub max_transactions_per_regular_sync_fetch: usize,
59
60    /// Time to wait during node start up until the node has synced the last
61    /// proposed block via the network peers. When set to `0` the sync
62    /// mechanism is disabled. This property is meant to be used for amnesia
63    /// recovery.
64    #[serde(default = "Parameters::default_sync_last_known_own_block_timeout")]
65    pub sync_last_known_own_block_timeout: Duration,
66
67    /// The number of rounds of blocks to be kept in the Dag state cache per
68    /// authority. The larger the number the more the blocks that will be
69    /// kept in memory allowing minimising any potential disk access.
70    /// Value should be at minimum 50 rounds to ensure node performance, but
71    /// being too large can be expensive in memory usage.
72    #[serde(default = "Parameters::default_dag_state_cached_rounds")]
73    pub dag_state_cached_rounds: u32,
74
75    // Number of authorities commit syncer fetches in parallel.
76    // Both commits in a range and blocks referenced by the commits are fetched per authority.
77    #[serde(default = "Parameters::default_commit_sync_parallel_fetches")]
78    pub commit_sync_parallel_fetches: usize,
79
80    // Number of commits to fetch in a batch, also the maximum number of commits returned per
81    // fetch. If this value is set too small, fetching becomes inefficient.
82    // If this value is set too large, it can result in load imbalance and stragglers.
83    #[serde(default = "Parameters::default_commit_sync_batch_size")]
84    pub commit_sync_batch_size: u32,
85
86    // This affects the maximum number of commit batches being fetched, and those fetched but not
87    // processed as consensus output, before throttling of outgoing commit fetches starts.
88    #[serde(default = "Parameters::default_commit_sync_batches_ahead")]
89    pub commit_sync_batches_ahead: usize,
90
91    /// Maximum number of headers to be included in a bundle. Headers exceeding
92    /// the max allowed limit will be truncated.
93    #[serde(default = "Parameters::default_max_headers_per_bundle")]
94    pub max_headers_per_bundle: usize,
95
96    /// Maximum number of transaction shards to be included in a bundle. Shards
97    /// exceeding the max allowed limit will be truncated.
98    #[serde(default = "Parameters::default_max_shards_per_bundle")]
99    pub max_shards_per_bundle: usize,
100
101    /// Tonic network settings.
102    #[serde(default = "TonicParameters::default")]
103    pub tonic: TonicParameters,
104
105    // Number of commits to fetch in a batch for fast commit syncer, also the maximum number of
106    // commits returned per fetch. If this value is set too small, fetching becomes
107    // inefficient. If this value is set too large, it can result in load imbalance and
108    // stragglers.
109    #[serde(default = "Parameters::default_fast_commit_sync_batch_size")]
110    pub fast_commit_sync_batch_size: u32,
111
112    // Gap threshold for switching between commit syncers. When the gap between quorum and local
113    // commit index is larger than this threshold, FastCommitSyncer fetches. Otherwise,
114    // CommitSyncer fetches.
115    #[serde(default = "Parameters::default_commit_sync_gap_threshold")]
116    pub commit_sync_gap_threshold: u32,
117
118    /// Enable FastCommitSyncer for faster recovery from large commit gaps.
119    /// This is a local node configuration that works in conjunction with the
120    /// protocol-level consensus_fast_commit_sync feature flag. Both must be
121    /// enabled for FastCommitSyncer to run. The protocol flag controls
122    /// whether gRPC endpoints are available, while this local flag controls
123    /// whether this specific node creates and runs the FastCommitSyncer.
124    /// Disabled by default; operators can enable it locally once the protocol
125    /// flag is active, or disable it again if bugs are discovered, without
126    /// affecting protocol-level endpoint availability.
127    #[serde(default = "Parameters::default_enable_fast_commit_syncer")]
128    pub enable_fast_commit_syncer: bool,
129}
130
131impl Parameters {
132    pub(crate) fn default_leader_timeout() -> Duration {
133        Duration::from_millis(250)
134    }
135
136    pub(crate) fn default_min_block_delay() -> Duration {
137        if cfg!(msim) || std::env::var("__TEST_ONLY_CONSENSUS_USE_LONG_MIN_BLOCK_DELAY").is_ok() {
138            // Checkpoint building and execution cannot keep up with high commit rate in
139            // simtests, leading to long reconfiguration delays. This is because
140            // simtest is single threaded, and spending too much time in
141            // consensus can lead to starvation elsewhere.
142            Duration::from_millis(400)
143        } else if cfg!(test) {
144            // Avoid excessive CPU, data and logs in tests.
145            Duration::from_millis(250)
146        } else {
147            // For production, use min delay between block being set to 50ms, reducing the
148            // block rate to 20 blocks/sec
149            Duration::from_millis(50)
150        }
151    }
152
153    pub(crate) fn default_max_forward_time_drift() -> Duration {
154        Duration::from_millis(500)
155    }
156
157    // Maximum number of block headers to fetch per commit sync request.
158    pub(crate) fn default_max_headers_per_commit_sync_fetch() -> usize {
159        if cfg!(msim) {
160            // Exercise hitting blocks per fetch limit.
161            10
162        } else {
163            1000
164        }
165    }
166
167    // Maximum number of transactions to fetch per commit sync request.
168    pub(crate) fn default_max_transactions_per_commit_sync_fetch() -> usize {
169        if cfg!(msim) {
170            // Exercise hitting transactions per fetch limit.
171            10
172        } else {
173            1000
174        }
175    }
176
177    // Maximum number of block headers to fetch per periodic or live sync request.
178    pub(crate) fn default_max_headers_per_regular_sync_fetch() -> usize {
179        if cfg!(msim) {
180            // Exercise hitting blocks per fetch limit.
181            10
182        } else {
183            // TODO: This might should match the value of block headers in the bundle.
184            100
185        }
186    }
187
188    // Maximum number of transactions to fetch per request.
189    pub(crate) fn default_max_transactions_per_regular_sync_fetch() -> usize {
190        if cfg!(msim) { 10 } else { 1000 }
191    }
192
193    pub(crate) fn default_sync_last_known_own_block_timeout() -> Duration {
194        if cfg!(msim) {
195            Duration::from_millis(500)
196        } else {
197            // Here we prioritise liveness over the complete de-risking of block
198            // equivocation. 5 seconds in the majority of cases should be good
199            // enough for this given a healthy network.
200            Duration::from_secs(5)
201        }
202    }
203
204    pub(crate) fn default_dag_state_cached_rounds() -> u32 {
205        if cfg!(msim) {
206            // Exercise reading blocks from store.
207            5
208        } else {
209            500
210        }
211    }
212
213    pub(crate) fn default_commit_sync_parallel_fetches() -> usize {
214        8
215    }
216
217    pub(crate) fn default_commit_sync_batch_size() -> u32 {
218        if cfg!(msim) {
219            // Exercise commit sync.
220            5
221        } else {
222            100
223        }
224    }
225
226    pub(crate) fn default_commit_sync_batches_ahead() -> usize {
227        // This is set to be a multiple of default commit_sync_parallel_fetches to allow
228        // fetching ahead, while keeping the total number of inflight fetches
229        // and unprocessed fetched commits limited.
230        32
231    }
232
233    pub(crate) fn default_max_headers_per_bundle() -> usize {
234        150
235    }
236
237    pub(crate) fn default_max_shards_per_bundle() -> usize {
238        150
239    }
240
241    pub(crate) fn default_fast_commit_sync_batch_size() -> u32 {
242        if cfg!(msim) {
243            // Exercise fast commit sync.
244            5
245        } else {
246            // With ~10KB per commit and 4MB max message size, 1000 commits (~10MB) requires
247            // chunking. The server will chunk commits across multiple response messages.
248            1000
249        }
250    }
251
252    pub(crate) fn default_commit_sync_gap_threshold() -> u32 {
253        if cfg!(msim) {
254            // Use smaller threshold for testing.
255            10
256        } else {
257            // When gap > 1000, FastCommitSyncer is more efficient.
258            // When gap <= 1000, CommitSyncer handles incremental sync.
259            1000
260        }
261    }
262
263    pub(crate) fn default_enable_fast_commit_syncer() -> bool {
264        // Disabled by default. Operators can enable it locally once the protocol-level
265        // consensus_fast_commit_sync flag is active, or disable it again if bugs are
266        // discovered, without waiting for a protocol upgrade.
267        false
268    }
269}
270
271impl Default for Parameters {
272    fn default() -> Self {
273        Self {
274            db_path: PathBuf::default(),
275            leader_timeout: Parameters::default_leader_timeout(),
276            min_block_delay: Parameters::default_min_block_delay(),
277            max_forward_time_drift: Parameters::default_max_forward_time_drift(),
278            max_headers_per_commit_sync_fetch:
279                Parameters::default_max_headers_per_commit_sync_fetch(),
280            max_transactions_per_commit_sync_fetch:
281                Parameters::default_max_transactions_per_commit_sync_fetch(),
282            max_headers_per_regular_sync_fetch:
283                Parameters::default_max_headers_per_regular_sync_fetch(),
284            max_transactions_per_regular_sync_fetch:
285                Parameters::default_max_transactions_per_regular_sync_fetch(),
286            sync_last_known_own_block_timeout:
287                Parameters::default_sync_last_known_own_block_timeout(),
288            dag_state_cached_rounds: Parameters::default_dag_state_cached_rounds(),
289            commit_sync_parallel_fetches: Parameters::default_commit_sync_parallel_fetches(),
290            commit_sync_batch_size: Parameters::default_commit_sync_batch_size(),
291            commit_sync_batches_ahead: Parameters::default_commit_sync_batches_ahead(),
292            max_headers_per_bundle: Parameters::default_max_headers_per_bundle(),
293            max_shards_per_bundle: Parameters::default_max_shards_per_bundle(),
294            tonic: TonicParameters::default(),
295            fast_commit_sync_batch_size: Parameters::default_fast_commit_sync_batch_size(),
296            commit_sync_gap_threshold: Parameters::default_commit_sync_gap_threshold(),
297            enable_fast_commit_syncer: Parameters::default_enable_fast_commit_syncer(),
298        }
299    }
300}
301
302#[derive(Clone, Debug, Deserialize, Serialize)]
303pub struct TonicParameters {
304    /// Keepalive interval and timeouts for both client and server.
305    ///
306    /// If unspecified, this will default to 5s.
307    #[serde(default = "TonicParameters::default_keepalive_interval")]
308    pub keepalive_interval: Duration,
309
310    /// Size of various per-connection buffers.
311    ///
312    /// If unspecified, this will default to 32MiB.
313    #[serde(default = "TonicParameters::default_connection_buffer_size")]
314    pub connection_buffer_size: usize,
315
316    /// Messages over this size threshold will increment a counter.
317    ///
318    /// If unspecified, this will default to 16MiB.
319    #[serde(default = "TonicParameters::default_excessive_message_size")]
320    pub excessive_message_size: usize,
321
322    /// Hard message size limit for both requests and responses.
323    /// This value is higher than strictly necessary, to allow overheads.
324    /// Message size targets and soft limits are computed based on this value.
325    ///
326    /// If unspecified, this will default to 1GiB.
327    #[serde(default = "TonicParameters::default_message_size_limit")]
328    pub message_size_limit: usize,
329}
330
331impl TonicParameters {
332    fn default_keepalive_interval() -> Duration {
333        Duration::from_secs(5)
334    }
335
336    fn default_connection_buffer_size() -> usize {
337        32 << 20
338    }
339
340    fn default_excessive_message_size() -> usize {
341        16 << 20
342    }
343
344    fn default_message_size_limit() -> usize {
345        64 << 20
346    }
347}
348
349impl Default for TonicParameters {
350    fn default() -> Self {
351        Self {
352            keepalive_interval: TonicParameters::default_keepalive_interval(),
353            connection_buffer_size: TonicParameters::default_connection_buffer_size(),
354            excessive_message_size: TonicParameters::default_excessive_message_size(),
355            message_size_limit: TonicParameters::default_message_size_limit(),
356        }
357    }
358}