Skip to content

Commit 6ae792a

Browse files
authored
refactor(configs): fork ShardingConfig for legacy and server-ng (#3633)
1 parent ebaffde commit 6ae792a

19 files changed

Lines changed: 520 additions & 404 deletions

File tree

core/configs/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ pub use server_config::{
3030
tcp, validators, websocket,
3131
};
3232
pub use server_ng_config::{
33-
COMPONENT_NG, message_bus, quic as ng_quic, server_ng, tcp as ng_tcp, websocket as ng_websocket,
33+
COMPONENT_NG, message_bus, quic as ng_quic, server_ng, sharding as ng_sharding, tcp as ng_tcp,
34+
websocket as ng_websocket,
3435
};

core/configs/src/server_config/defaults.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ use super::server::{
2626
PersonalAccessTokenConfig, ServerConfig, TelemetryConfig, TelemetryLogsConfig,
2727
TelemetryTracesConfig,
2828
};
29-
use super::sharding::ShardingConfig;
3029
use super::system::{
3130
BackupConfig, CompatibilityConfig, CompressionConfig, EncryptionConfig, LoggingConfig,
3231
MessageDeduplicationConfig, PartitionConfig, RecoveryConfig, RuntimeConfig, SegmentConfig,
@@ -35,6 +34,7 @@ use super::system::{
3534
use super::tcp::TcpSocketConfig;
3635
use super::tcp::{TcpConfig, TcpTlsConfig};
3736
use super::websocket::{WebSocketConfig, WebSocketTlsConfig};
37+
use configs::ConfigEnvMappings;
3838
use iggy_common::IggyByteSize;
3939
use iggy_common::IggyDuration;
4040
use std::sync::Arc;
@@ -321,9 +321,9 @@ impl Default for PersonalAccessTokenCleanerConfig {
321321
}
322322
}
323323

324-
impl Default for SystemConfig {
325-
fn default() -> SystemConfig {
326-
SystemConfig {
324+
impl<S: ConfigEnvMappings + Default> Default for SystemConfig<S> {
325+
fn default() -> Self {
326+
Self {
327327
path: SERVER_CONFIG.system.path.parse().unwrap(),
328328
backup: BackupConfig::default(),
329329
runtime: RuntimeConfig::default(),
@@ -338,7 +338,7 @@ impl Default for SystemConfig {
338338
message_deduplication: MessageDeduplicationConfig::default(),
339339
recovery: RecoveryConfig::default(),
340340
memory_pool: MemoryPoolConfig::default(),
341-
sharding: ShardingConfig::default(),
341+
sharding: S::default(),
342342
}
343343
}
344344
}

core/configs/src/server_config/displays.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use super::{
3030
},
3131
tcp::{TcpConfig, TcpSocketConfig, TcpTlsConfig},
3232
};
33+
use configs::ConfigEnvMappings;
3334
use std::fmt::{Display, Formatter};
3435

3536
impl Display for HttpConfig {
@@ -345,7 +346,7 @@ impl Display for TelemetryTracesConfig {
345346
}
346347
}
347348

348-
impl Display for SystemConfig {
349+
impl<S: ConfigEnvMappings> Display for SystemConfig<S> {
349350
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
350351
write!(
351352
f,

core/configs/src/server_config/sharding.rs

Lines changed: 5 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,9 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18-
use iggy_common::IggyDuration;
1918
use serde::{Deserialize, Serialize};
20-
use serde_with::{DisplayFromStr, serde_as};
21-
use std::time::Duration;
2219

20+
use super::defaults::SERVER_CONFIG;
2321
use configs::ConfigEnv;
2422

2523
// `CpuAllocation`/`NumaConfig` are pure config types and live in their own
@@ -28,96 +26,9 @@ use configs::ConfigEnv;
2826
// `configs::sharding::*` path stable for existing callers.
2927
pub use cpu_allocation::{CpuAllocation, NumaConfig};
3028

31-
/// Default capacity of the per-shard inter-shard inbox channel. Sized
32-
/// comfortably above the consensus working set, which is roughly
33-
/// `PIPELINE_PREPARE_QUEUE_MAX (= 32) * replica_count * directions`
34-
/// frames in flight per shard, without allowing a runaway producer to
35-
/// eat unbounded memory. Tunable via `[system.sharding] inbox_capacity`
36-
/// in TOML.
37-
///
38-
/// The capacity must also absorb the worst-case cross-shard client
39-
/// Reply burst. Unlike consensus frames, client Replies have no VSR
40-
/// retransmit path: a Reply lost on full inbox is gone and the client
41-
/// times out. A reasonable lower bound is
42-
/// `max_inflight_client_requests / num_shards` (assuming requests are
43-
/// distributed evenly across owning shards) plus the consensus
44-
/// headroom above.
45-
///
46-
/// Consensus frames and client-reply forwards share this one channel,
47-
/// so the two headrooms are not independent: a consensus burst or
48-
/// retransmit storm can fill the inbox with consensus frames exactly
49-
/// when a client Reply needs the space. A single `inbox_capacity` knob
50-
/// cannot isolate the two frame classes - size it for the sum of both
51-
/// worst cases occurring together. Watch the drop-site `tracing` logs
52-
/// (and, once a per-shard exporter lands, the `frame_drops_total`
53-
/// `{variant="forward_client_send"}` counter) to detect when the bound
54-
/// is too low in production.
55-
pub const DEFAULT_INBOX_CAPACITY: usize = 1024;
56-
57-
/// Maximum permitted per-shard inbox depth. The channel is allocated
58-
/// up-front per shard, so a runaway value here OOMs the process at boot.
59-
/// `1 << 20` (~1M frames) is several orders of magnitude above any
60-
/// realistic backpressure target and still fits comfortably in process
61-
/// address space.
62-
pub const INBOX_CAPACITY_MAX: usize = 1 << 20;
63-
64-
/// Default bus shutdown drain timeout. Sized larger than typical TCP RTT
65-
/// times in-flight write-batch so writers receive their full last
66-
/// `write_vectored_all` budget before the connection registry kicks in.
67-
pub const DEFAULT_SHUTDOWN_DRAIN_TIMEOUT: Duration = Duration::from_secs(10);
68-
69-
/// Default watchdog poll cadence for the cross-thread shutdown flag.
70-
/// 50ms keeps Ctrl-C latency operator-visible without measurable wakeup
71-
/// overhead.
72-
pub const DEFAULT_SHUTDOWN_POLL_INTERVAL: Duration = Duration::from_millis(50);
73-
74-
/// Hard upper bound on `shutdown_drain_timeout`. A drain that never
75-
/// completes wedges process exit; capping at 10 minutes guarantees the
76-
/// watchdog eventually force-tears the bus even with a pathological
77-
/// config typo.
78-
pub const SHUTDOWN_DRAIN_TIMEOUT_MAX: Duration = Duration::from_secs(600);
79-
80-
/// Hard upper bound on `shutdown_poll_interval`. A pollerinterval longer
81-
/// than the drain timeout makes the flag effectively unobservable; cap
82-
/// at 5s so Ctrl-C latency stays bounded regardless of config.
83-
pub const SHUTDOWN_POLL_INTERVAL_MAX: Duration = Duration::from_secs(5);
84-
85-
/// Default safety-tick cadence for the partition reconciliation loop.
86-
/// The reconciler also wakes on every `LifecycleFrame::MetadataCommitTick`
87-
/// broadcast by shard 0; this fallback covers dropped wake-ups (the wake
88-
/// channel is intentionally capacity-1) and the initial post-bootstrap
89-
/// convergence window before shard 0's first tick. One second is
90-
/// invisible to operators yet keeps idle clusters from burning CPU
91-
/// re-reading the same target snapshot.
92-
pub const DEFAULT_RECONCILE_PERIODIC_INTERVAL: Duration = Duration::from_secs(1);
93-
94-
/// Hard upper bound on `reconcile_periodic_interval`. A tick longer
95-
/// than ~30s makes post-failure recovery latency operator-visible; the
96-
/// cap reins in pathological typos without disturbing reasonable
97-
/// production values.
98-
pub const RECONCILE_PERIODIC_INTERVAL_MAX: Duration = Duration::from_secs(30);
99-
100-
const fn default_inbox_capacity() -> usize {
101-
DEFAULT_INBOX_CAPACITY
102-
}
103-
104-
const fn default_pin_cores() -> bool {
105-
true
106-
}
107-
108-
fn default_shutdown_drain_timeout() -> IggyDuration {
109-
IggyDuration::new(DEFAULT_SHUTDOWN_DRAIN_TIMEOUT)
110-
}
111-
112-
fn default_shutdown_poll_interval() -> IggyDuration {
113-
IggyDuration::new(DEFAULT_SHUTDOWN_POLL_INTERVAL)
114-
}
115-
116-
fn default_reconcile_periodic_interval() -> IggyDuration {
117-
IggyDuration::new(DEFAULT_RECONCILE_PERIODIC_INTERVAL)
118-
}
119-
120-
#[serde_as]
29+
/// Sharding config for the legacy `core/server`. That server consumes only
30+
/// `cpu_allocation` and `pin_cores`; the bus / shutdown / reconcile knobs are
31+
/// server-ng concepts and live in [`crate::server_ng_config::sharding`].
12132
#[derive(Debug, Deserialize, Serialize, ConfigEnv)]
12233
pub struct ShardingConfig {
12334
#[serde(default)]
@@ -133,70 +44,14 @@ pub struct ShardingConfig {
13344
/// threads freely within the allowed set. With a NUMA-aware allocation,
13445
/// `false` drops both the CPU and memory-node bindings (and logs a
13546
/// warning, since NUMA placement without pinning is meaningless).
136-
#[serde(default = "default_pin_cores")]
13747
pub pin_cores: bool,
138-
/// Per-shard inter-shard inbox channel capacity. Bounded by design.
139-
/// Drops on full inbox of consensus frames are recovered by VSR
140-
/// retransmit. Drops of cross-shard client Reply frames are terminal:
141-
/// the client never receives the reply (no in-protocol retransmit).
142-
/// Both frame classes share this one channel, so a consensus burst
143-
/// can starve client-reply forwards: size against the worst-case sum
144-
/// of consensus working set + peak client-reply fan-out per shard
145-
/// occurring together; see `DEFAULT_INBOX_CAPACITY` for the
146-
/// rationale. Used by `core/server-ng`; the legacy server uses its
147-
/// own hard-coded inbox sizing.
148-
///
149-
// TODO(hubcio): split into two priority lanes - one bounded queue for
150-
// consensus frames (drops recovered by VSR retransmit) and one for
151-
// client `Reply` frames (drops terminal, must be sized for worst-case
152-
// fan-out). Current single-channel design is the minimum-viable
153-
// wiring so `frame_drops_total{variant,reason}` surfaces under load
154-
// and yields real numbers to size the split against.
155-
#[serde(default = "default_inbox_capacity")]
156-
pub inbox_capacity: usize,
157-
/// Wall-clock budget for a single shard's bus drain on shutdown.
158-
/// Drives `IggyMessageBus::shutdown(..)` from the per-shard watchdog
159-
/// and the parallel-join survivor path. Sized larger than typical
160-
/// TCP RTT times in-flight write-batch so writers receive their full
161-
/// last `write_vectored_all` budget before the connection registry
162-
/// force-tears the bus. Slow-fsync hosts may need to extend this past
163-
/// the default; the cap is `SHUTDOWN_DRAIN_TIMEOUT_MAX` so a config
164-
/// typo cannot wedge process exit.
165-
#[serde(default = "default_shutdown_drain_timeout")]
166-
#[serde_as(as = "DisplayFromStr")]
167-
#[config_env(leaf)]
168-
pub shutdown_drain_timeout: IggyDuration,
169-
/// Poll cadence for the cross-thread shutdown flag and for the
170-
/// `await_metadata_bundle` / `broadcast_metadata_bundle` poll loops.
171-
/// Trades off Ctrl-C latency against idle wakeup cost; the default
172-
/// keeps shutdown observably prompt without measurable scheduler
173-
/// overhead. Capped at `SHUTDOWN_POLL_INTERVAL_MAX` so the flag
174-
/// remains effectively observable regardless of config.
175-
#[serde(default = "default_shutdown_poll_interval")]
176-
#[serde_as(as = "DisplayFromStr")]
177-
#[config_env(leaf)]
178-
pub shutdown_poll_interval: IggyDuration,
179-
/// Safety-tick cadence for the partition reconciliation loop; the
180-
/// reconciler also wakes immediately on every
181-
/// `LifecycleFrame::MetadataCommitTick` from shard 0. See
182-
/// [`DEFAULT_RECONCILE_PERIODIC_INTERVAL`] for the rationale; values
183-
/// above [`RECONCILE_PERIODIC_INTERVAL_MAX`] are rejected by the
184-
/// validator.
185-
#[serde(default = "default_reconcile_periodic_interval")]
186-
#[serde_as(as = "DisplayFromStr")]
187-
#[config_env(leaf)]
188-
pub reconcile_periodic_interval: IggyDuration,
18948
}
19049

19150
impl Default for ShardingConfig {
19251
fn default() -> Self {
19352
Self {
19453
cpu_allocation: CpuAllocation::default(),
195-
pin_cores: default_pin_cores(),
196-
inbox_capacity: DEFAULT_INBOX_CAPACITY,
197-
shutdown_drain_timeout: default_shutdown_drain_timeout(),
198-
shutdown_poll_interval: default_shutdown_poll_interval(),
199-
reconcile_periodic_interval: default_reconcile_periodic_interval(),
54+
pin_cores: SERVER_CONFIG.system.sharding.pin_cores,
20055
}
20156
}
20257
}

core/configs/src/server_config/system.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use super::cache_indexes::CacheIndexesConfig;
1919
use super::server::MemoryPoolConfig;
2020
use super::sharding::ShardingConfig;
21-
use configs::ConfigEnv;
21+
use configs::{ConfigEnv, ConfigEnvMappings};
2222
use iggy_common::IggyByteSize;
2323
use iggy_common::IggyError;
2424
use iggy_common::IggyExpiry;
@@ -33,8 +33,13 @@ use server_common::log::LoggingSettings;
3333
pub const INDEX_EXTENSION: &str = "index";
3434
pub const LOG_EXTENSION: &str = "log";
3535

36+
// Generic over the sharding config so the legacy server and `server-ng` each
37+
// bind their own `ShardingConfig` (different knob sets, different default
38+
// source) while sharing this whole struct and its path helpers. The default
39+
// type param keeps bare `SystemConfig` meaning the legacy variant, so existing
40+
// callers compile unchanged.
3641
#[derive(Debug, Deserialize, Serialize, ConfigEnv)]
37-
pub struct SystemConfig {
42+
pub struct SystemConfig<S: ConfigEnvMappings = ShardingConfig> {
3843
pub path: String,
3944
pub backup: BackupConfig,
4045
pub state: StateConfig,
@@ -49,7 +54,7 @@ pub struct SystemConfig {
4954
pub message_deduplication: MessageDeduplicationConfig,
5055
pub recovery: RecoveryConfig,
5156
pub memory_pool: MemoryPoolConfig,
52-
pub sharding: ShardingConfig,
57+
pub sharding: S,
5358
}
5459

5560
#[derive(Debug, Deserialize, Serialize, ConfigEnv)]
@@ -188,7 +193,7 @@ pub struct StateConfig {
188193
pub retry_delay: IggyDuration,
189194
}
190195

191-
impl SystemConfig {
196+
impl<S: ConfigEnvMappings> SystemConfig<S> {
192197
pub fn get_system_path(&self) -> String {
193198
self.path.to_string()
194199
}
@@ -352,7 +357,7 @@ impl SystemConfig {
352357
}
353358
}
354359

355-
impl SystemPaths for SystemConfig {
360+
impl<S: ConfigEnvMappings> SystemPaths for SystemConfig<S> {
356361
fn get_system_path(&self) -> String {
357362
SystemConfig::get_system_path(self)
358363
}

0 commit comments

Comments
 (0)