|
| 1 | +use core::num::NonZeroU64; |
| 2 | +use init4_bin_base::utils::from_env::FromEnv; |
| 3 | +use signet_journal_chain::SAFETY_MARGIN; |
| 4 | +use tracing::warn; |
| 5 | + |
| 6 | +/// Default maximum total byte size of the journal ring buffer (64 MiB). |
| 7 | +pub const DEFAULT_RING_BUFFER_MAX_BYTES: u64 = 64 * 1024 * 1024; |
| 8 | + |
| 9 | +/// Default maximum number of journals held in the ring buffer. Must be at |
| 10 | +/// least [`SAFETY_MARGIN`]; smaller values are clamped up by the chain. |
| 11 | +pub const DEFAULT_RING_BUFFER_MAX_COUNT: u64 = 200; |
| 12 | + |
| 13 | +const _: () = assert!( |
| 14 | + DEFAULT_RING_BUFFER_MAX_COUNT >= SAFETY_MARGIN, |
| 15 | + "DEFAULT_RING_BUFFER_MAX_COUNT must be at least signet_journal_chain::SAFETY_MARGIN" |
| 16 | +); |
| 17 | + |
| 18 | +/// Default broadcast-subscriber lag tolerance (in journals) before the |
| 19 | +/// chain disconnects a slow subscriber. |
| 20 | +pub const DEFAULT_MAX_SUBSCRIBER_LAG: u64 = 100; |
| 21 | + |
| 22 | +/// Configuration settings for the embedded journal chain. |
| 23 | +/// |
| 24 | +/// All fields are optional. When unset, [`JournalConfig`] returns the |
| 25 | +/// constants above via its accessors. Configurable via environment variables |
| 26 | +/// (`SIGNET_JOURNAL_*`) or via serde for file-based config. |
| 27 | +#[derive(Debug, Clone, Copy, Default, serde::Deserialize, FromEnv)] |
| 28 | +#[serde(rename_all = "camelCase", default)] |
| 29 | +pub struct JournalConfig { |
| 30 | + /// Maximum total byte size of the journal ring buffer. |
| 31 | + #[from_env( |
| 32 | + var = "SIGNET_JOURNAL_RING_BUFFER_MAX_BYTES", |
| 33 | + desc = "Journal ring buffer byte limit [default: 67108864 (64 MiB)]", |
| 34 | + optional |
| 35 | + )] |
| 36 | + ring_buffer_max_bytes: Option<u64>, |
| 37 | + |
| 38 | + /// Maximum number of journals in the ring buffer. Values below the |
| 39 | + /// chain's `SAFETY_MARGIN` are clamped up. |
| 40 | + #[from_env( |
| 41 | + var = "SIGNET_JOURNAL_RING_BUFFER_MAX_COUNT", |
| 42 | + desc = "Journal ring buffer count limit [default: 200]", |
| 43 | + optional |
| 44 | + )] |
| 45 | + ring_buffer_max_count: Option<u64>, |
| 46 | + |
| 47 | + /// Maximum number of journals a `/journal` WebSocket subscriber may lag |
| 48 | + /// behind the broadcast tip before the chain closes the connection with |
| 49 | + /// a `Lagged` (4003) close frame. Zero is normalized to the default |
| 50 | + /// because the chain requires a non-zero value. |
| 51 | + #[from_env( |
| 52 | + var = "SIGNET_JOURNAL_MAX_SUBSCRIBER_LAG", |
| 53 | + desc = "Journal subscriber lag tolerance [default: 100, 0 means use default]", |
| 54 | + optional |
| 55 | + )] |
| 56 | + max_subscriber_lag: Option<u64>, |
| 57 | +} |
| 58 | + |
| 59 | +impl JournalConfig { |
| 60 | + /// Maximum total byte size of the ring buffer, falling back to |
| 61 | + /// [`DEFAULT_RING_BUFFER_MAX_BYTES`]. |
| 62 | + pub const fn ring_buffer_max_bytes(&self) -> u64 { |
| 63 | + match self.ring_buffer_max_bytes { |
| 64 | + Some(bytes) => bytes, |
| 65 | + None => DEFAULT_RING_BUFFER_MAX_BYTES, |
| 66 | + } |
| 67 | + } |
| 68 | + |
| 69 | + /// Maximum ring buffer entry count, falling back to |
| 70 | + /// [`DEFAULT_RING_BUFFER_MAX_COUNT`]. |
| 71 | + pub const fn ring_buffer_max_count(&self) -> u64 { |
| 72 | + match self.ring_buffer_max_count { |
| 73 | + Some(count) => count, |
| 74 | + None => DEFAULT_RING_BUFFER_MAX_COUNT, |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + /// Subscriber lag tolerance, falling back to |
| 79 | + /// [`DEFAULT_MAX_SUBSCRIBER_LAG`]. Zero is normalized to the default |
| 80 | + /// because the chain requires a non-zero value. |
| 81 | + pub const fn max_subscriber_lag(&self) -> NonZeroU64 { |
| 82 | + let value = match self.max_subscriber_lag { |
| 83 | + Some(0) | None => DEFAULT_MAX_SUBSCRIBER_LAG, |
| 84 | + Some(lag) => lag, |
| 85 | + }; |
| 86 | + NonZeroU64::new(value).expect("DEFAULT_MAX_SUBSCRIBER_LAG is non-zero") |
| 87 | + } |
| 88 | + |
| 89 | + /// Emit a warning for any field that is explicitly set to a value the |
| 90 | + /// journal chain will silently normalize. Covers a zero |
| 91 | + /// `max_subscriber_lag` (which the chain rejects, so the default is |
| 92 | + /// substituted) and a `ring_buffer_max_count` below [`SAFETY_MARGIN`] |
| 93 | + /// (which the chain clamps up). Intended to be called once at startup. |
| 94 | + pub fn warn_on_misconfiguration(&self) { |
| 95 | + if self.max_subscriber_lag == Some(0) { |
| 96 | + warn!( |
| 97 | + default = DEFAULT_MAX_SUBSCRIBER_LAG, |
| 98 | + "SIGNET_JOURNAL_MAX_SUBSCRIBER_LAG=0 is not a valid lag tolerance; \ |
| 99 | + falling back to the default" |
| 100 | + ); |
| 101 | + } |
| 102 | + if let Some(configured) = self.ring_buffer_max_count |
| 103 | + && configured < SAFETY_MARGIN |
| 104 | + { |
| 105 | + warn!( |
| 106 | + configured, |
| 107 | + safety_margin = SAFETY_MARGIN, |
| 108 | + "SIGNET_JOURNAL_RING_BUFFER_MAX_COUNT is below the journal chain's safety \ |
| 109 | + margin and will be clamped up" |
| 110 | + ); |
| 111 | + } |
| 112 | + } |
| 113 | +} |
0 commit comments