Skip to main content

ServerConfig

Struct ServerConfig 

Source
pub struct ServerConfig {
Show 39 fields pub bind: SocketAddr, pub journal: PathBuf, pub snapshot: Option<PathBuf>, pub cores: PipelineCores, pub group_commit_us: u64, pub heartbeat_interval_secs: u64, pub connection_timeout_secs: u64, pub max_connections: u64, pub authorized_keys: PathBuf, pub max_journal_mib: u64, pub journal_staging_mode: JournalStagingMode, pub replication_bind: Option<SocketAddr>, pub standalone: bool, pub replica_of: Option<SocketAddr>, pub replication_key: Option<PathBuf>, pub replication_batch_size: usize, pub max_journal_batch: usize, pub replication_heartbeat_secs: u64, pub replication_pipeline_depth: usize, pub replication_ring_size: usize, pub ack_policy: AckPolicy, pub no_degraded_acks: bool, pub degraded_ack_grace_ms: u64, pub dpdk: DpdkArgs, pub dpdk_client_rx_buf_kib: u32, pub dpdk_client_tx_buf_kib: u32, pub dpdk_client_tx_queue_kib: u32, pub event_bind: Option<SocketAddr>, pub health_bind: Option<SocketAddr>, pub admin_bind: Option<SocketAddr>, pub snapshot_interval_ms: u64, pub snapshot_path: Option<PathBuf>, pub tick_interval_ms: u64, pub no_mlock: bool, pub raft_bind: Option<SocketAddr>, pub raft_node_id: Option<u64>, pub raft_peer: Vec<String>, pub raft_dir: Option<PathBuf>, pub raft_auto_promote: bool,
}
Expand description

Server configuration, parsed from CLI arguments via clap.

Fields§

§bind: SocketAddr

Address to bind the TCP listener.

§journal: PathBuf

Path to the journal file for durable event sourcing.

§snapshot: Option<PathBuf>

Path to a snapshot file for faster recovery.

§cores: PipelineCores

Where each pipeline thread runs, as comma-separated thread=core entries in any order. Threads: journal-seq, matching, response, reader, event-publisher, shadow, repl-handler-0, repl-handler-1, journal-prep, journal-disk. Every thread must be named: 0 leaves one unpinned, and none unpins every thread. The node warns at boot when journal-seq, matching, response, reader or journal-disk has no core: every request passes through them. Core 0 is reserved for OS/IRQ handling. reader pins the io_uring reader (TCP) or DPDK poll thread. event-publisher applies when --event-bind is set, shadow when --snapshot-interval-ms > 0. repl-handler-0/1 are for the per-replica TCP handler threads. journal-disk pins the thread that writes and syncs the journal; give it a core on the same CCD as journal-seq, since the two exchange a cache line per batch.

Each core may carry a suffix saying how that thread waits: 7 (or 7s) busy-spins and needs the core to itself, 7y spins briefly then yields and may share the core with other y threads. 0 (unpinned) always yields. Two threads on one core where either busy-spins is refused at startup — they would starve each other. journal-prep never busy-waits and takes no suffix.

§group_commit_us: u64

Group commit coalescing delay in microseconds. Keep at 0 for TCP.

§heartbeat_interval_secs: u64

Heartbeat interval in seconds. The server sends a heartbeat to idle connections after this many seconds of silence. Set to 0 to disable.

§connection_timeout_secs: u64

Connection timeout in seconds. The server disconnects clients that have not sent any data within this window. Set to 0 to disable.

§max_connections: u64

Maximum number of concurrent authenticated connections, from 1 to 8192. New connections are rejected (closed immediately) when this limit is reached. Prevents fd/memory exhaustion (SEC-02). The io_uring rings are sized from it, and their memory counts against the locked-memory limit, so a cap well above the clients the node serves costs memory for nothing. There is no “unlimited”.

§authorized_keys: PathBuf

Path to the authorized keys file for Ed25519 challenge-response authentication. Every connection must authenticate before sending requests. Required for primary mode; ignored in replica mode (–replica-of). See AuthorizedKeys for file format.

§max_journal_mib: u64

Maximum journal size in MiB before automatic rotation at startup. When the journal exceeds this threshold, the server saves a snapshot and starts a fresh journal. Set to 0 to disable. Default: 256 MiB.

§journal_staging_mode: JournalStagingMode

How the background preparer materialises the next journal segment’s extents.

  • zero-fill (default) physically pre-writes the segment, so appends carry no extent-conversion metadata and each flush’s fdatasync never forces the filesystem log. It costs one extra pass over every segment in device bandwidth.
  • allocate reserves the extents and stops. Staging becomes free, and the periodic log force returns to the flush path.

Keep the default on local NVMe, where sequential bandwidth is cheap and the log forces are what hurts. Consider allocate on network-attached storage (EBS and similar), where staging bandwidth is metered and drawn from the same budget as the hot path — measure both on the volume in question rather than assuming, since which one wins is a property of the device.

§replication_bind: Option<SocketAddr>

Address to listen for replica connections (enables synchronous replication). Mutually exclusive with --standalone and --replica-of.

§standalone: bool

Disable replication (dev/test mode). The replica quorum cursor stays at its no-replica sentinel (health reports zero replication lag) and the ack policy evaluates against the primary’s journal alone. Mutually exclusive with --replication-bind and --replica-of.

§replica_of: Option<SocketAddr>

Run as a replica connected to the given primary address. In replica mode, the server does not accept client connections. Mutually exclusive with --replication-bind and --standalone.

§replication_key: Option<PathBuf>

Path to the Ed25519 private key for replication authentication. Required in replica mode (--replica-of). The corresponding public key must be listed in the primary’s authorized_keys file under the replication role.

§replication_batch_size: usize

Maximum number of replication ring batches to coalesce into a single TCP write+flush. Higher values reduce syscall overhead but increase per-write latency. Default: 128.

§max_journal_batch: usize

Maximum events per journal fsync batch. Smaller values reduce tail latency (less work per sync), larger values improve throughput (fewer fsyncs). Default: 4096.

§replication_heartbeat_secs: u64

Replication heartbeat interval in seconds. The primary sends a heartbeat to the replica after this many seconds of idle. Used for disconnect detection. Default: 5.

§replication_pipeline_depth: usize

Number of receive batches the replica tracks individually while they await local journal fsync. Must be a power of two. Beyond this depth the receiver keeps receiving and coalesces pending acks (a later ack, never an early one) — it never applies backpressure to the primary. Default: 256.

§replication_ring_size: usize

Number of slots in each replication ring buffer. Must be a power of two. Each slot holds up to 512 KiB. More slots = more buffering before eviction. Default: 256 (128 MiB per ring, 256 MiB dual-repl).

§ack_policy: AckPolicy

Ack policy: which copies of an event must exist before its response is released. One of:

  • disk persisted>=1. One fsynced copy, on whichever node’s disk confirms first; required with --standalone. Dev/staging deployments.
  • ram in_memory>=2. Two nodes hold the event in memory before the ack; every fsync trails off the ack path (the journal still syncs every batch). Lowest ack latency; survives any single node failure via failover; loses only the un-fsynced tail on a whole-cluster power loss. For slow-fsync storage (cloud volumes) or RPO-tolerant apps.
  • disk+ram (default) persisted>=1 && in_memory>=2. One fsynced copy plus a second copy in another node’s memory. Single-failure-safe with a brief RAM-only window for the second copy. Typical live deployments. Faster than two-disks: an acknowledgement waits for the second node to receive the event, not to fsync it.
  • two-disks persisted>=2. Two fsynced copies before the client ack. Zero RAM-only window; the gate stalls when no replica is connected. Compliance-driven deployments.

--standalone requires disk. Under every other policy the gate stalls while no replica is connected — the correct behaviour for a serious deployment that has lost its replicas. See docs/replication.md for the operational menu.

§no_degraded_acks: bool

Keep a halted primary’s held replies waiting for a replica.

A primary that loses its last replica halts: it refuses new writes. The replies it holds for the ack policy — writes it took before the halt, and queries — cannot be confirmed by a replica while none is connected. By default, once the halt has lasted --degraded-ack-grace-ms, each is sent as soon as this node’s own journal holds it, ending in BatchEndDegraded: the client is told only the primary’s disk backs it. With this flag every such reply, and every refusal and query queued behind one, waits for a replica to return or the operator to swap the policy — strict fail-closed, with clients seeing only their own read timeout.

§degraded_ack_grace_ms: u64

How long, in milliseconds, a primary must have had no replica before it answers held replies on its own disk (see --no-degraded-acks). A replica that comes back within it, and catches up, confirms them in full.

§dpdk: DpdkArgs

The --dpdk-* flags (only used with –features dpdk), declared in melin-dpdk for the node and its clients alike. On the node --dpdk-ip defaults to 10.0.0.1, NicConfig::default()’s address, where a client requires it.

§dpdk_client_rx_buf_kib: u32

Receive buffer, in KiB, of each client connection on the DPDK client port: the window advertised to the client.

§dpdk_client_tx_buf_kib: u32

Send buffer, in KiB, of each client connection on the DPDK client port: the in-flight window, which caps one connection’s response bandwidth at this much per round trip. The default carries one client at a million responses a second with headroom; a single connection driven at the stack’s full rate wants more (256). Every client connection holds one, so it multiplies by their number.

§dpdk_client_tx_queue_kib: u32

Responses, in KiB, that may queue behind a client connection’s send buffer before the connection is dropped as fallen behind.

§event_bind: Option<SocketAddr>

Address for the output event publisher. Subscribers connect here to receive a real-time stream of the application’s reports, as the application’s publisher encodes them. Ed25519 auth against the node’s authorized_keys; which keys may subscribe, and what each may see, is the application’s publisher’s decision. Omit to disable (ring has 1 consumer — identical to before).

§health_bind: Option<SocketAddr>

Address for the health/liveness TCP endpoint. On connect, returns a one-line status (OK <conns> <journal_seq> <repl_lag>\n) and closes. No auth required. Set to empty string to disable.

§admin_bind: Option<SocketAddr>

TCP address for the operator admin endpoint. Authenticated with operator keys (Ed25519 challenge-response, same as all other admin handshakes). Accepts:

  • PROMOTE\n — replica → primary leadership transition (replica nodes only; rejected with ERR on a primary).
  • ROTATE\n — archive the current journal segment at the next fsync boundary (any node where runtime rotation is enabled).

Unset means no admin endpoint is listening; operators can still rely on the size-driven rotation trigger (--max-journal-mib) without exposing a port.

§snapshot_interval_ms: u64

Interval in milliseconds between automatic shadow snapshots. The shadow stage replays journaled events on a cloned A and saves a consistent snapshot at this cadence — no hot-path stall. Set to 0 to disable shadow snapshots entirely. Default: 3 000 000 ms (50 min).

§snapshot_path: Option<PathBuf>

Path for shadow snapshots. Defaults to the journal path with a .snapshot extension (same as the startup snapshot path).

§tick_interval_ms: u64

Cadence in milliseconds for the application’s clock tick. The ingress thread (io_uring reader or DPDK poll thread) publishes a JournalEvent::Tick { now_ns } at this interval so the application’s time-driven work (expiries, timeouts, scheduled transitions) fires in deterministic, journaled lockstep. There is no separate tick thread on either transport. Set to 0 to disable tick generation entirely (useful for benchmarks that don’t exercise time-driven features).

Defaults to 250 ms. Under load the matching stage advances the application’s clock at every-event resolution from slot.timestamp_ns (microsecond precision), so the tick is only the safety net for quiet periods. 250 ms keeps time-driven work in quiet periods within a quarter-second of their deadline at a cost of ~4 events/sec of journal traffic.

§no_mlock: bool

Disable mlockall(MCL_CURRENT | MCL_FUTURE) at startup.

By default the server locks all current and future pages into RAM to prevent rare multi-millisecond stalls from page faults or swap activity on the matching hot path. Locking requires CAP_IPC_LOCK (or running as root) and RLIMIT_MEMLOCK raised to a value larger than the process’s RSS — the server raises the rlimit itself, but only succeeds with the capability.

Use --no-mlock for development / containerised runs where the privilege isn’t available; on a bare-metal production host leave it on.

§raft_bind: Option<SocketAddr>

Bind address for the control-plane raft RPC listener. Setting this enables the control plane on this node: leader election runs and is observable via the melin_raft_* gauges. Requires --raft-node-id and a --raft-peer entry for this node; requires --replication-key (peer links authenticate with the replication key, same trust domain as the data plane). Raft always runs on kernel TCP, including on DPDK nodes.

§raft_node_id: Option<u64>

This node’s control-plane raft id (non-zero, unique per cluster).

§raft_peer: Vec<String>

A control-plane cluster member as id@host:port#base64-pubkey (repeatable). Give every node the same list, including an entry for the node itself — the self entry supplies the dialable address peers use to reach it, and identical lists keep the first-boot membership consistent. The pubkey pins the peer’s identity: a connection authenticated with a different key cannot speak for that id.

§raft_dir: Option<PathBuf>

Directory for durable raft state (vote, log, membership). Defaults to the journal path with a .raft extension. Must live on the same durability class as the journal — losing it can double-grant a vote (see docs/replication.md).

§raft_auto_promote: bool

Act on control-plane election wins: a replica elected leader promotes itself (journaling the election term as its fencing epoch), and a serving node self-fences when the raft mesh shows it superseded. Off by default — automatic failover is an explicit operator policy decision. Requires --raft-bind and at least three configured voters (a two-node cluster cannot elect after losing either node, so automation would be dead weight with real misconfiguration risk; see docs/replication.md).

Implementations§

Source§

impl ServerConfig

Source

pub fn group_commit_delay(&self) -> Duration

Group commit delay as a Duration.

Source

pub fn heartbeat_interval(&self) -> Option<Duration>

Heartbeat interval as a Duration. Returns None if disabled (0).

Source

pub fn connection_timeout(&self) -> Option<Duration>

Connection timeout as a Duration. Returns None if disabled (0).

Source

pub fn shadow_snapshot_path(&self) -> PathBuf

Snapshot path for the shadow stage. Uses the explicit --snapshot-path if set, otherwise derives from the journal path with .snapshot extension.

Source

pub fn tick_interval(&self) -> Option<Duration>

Tick generator cadence as a Duration. Returns None when the tick thread is disabled (--tick-interval-ms 0).

Source

pub fn resolved_cores( &self, reader: ReaderThread, ) -> Result<PipelineCores, String>

The per-thread layout this node actually runs — see PipelineCores::resolve. reader says which thread the reader entry pins on this transport, since one of the two cannot yield.

An Err is a configuration the node refuses to start with.

Trait Implementations§

Source§

impl Args for ServerConfig

Source§

fn group_id() -> Option<Id>

Report the ArgGroup::id for this set of arguments
Source§

fn augment_args<'b>(__clap_app: Command) -> Command

Append to Command so it can instantiate Self via FromArgMatches::from_arg_matches_mut Read more
Source§

fn augment_args_for_update<'b>(__clap_app: Command) -> Command

Append to Command so it can instantiate self via FromArgMatches::update_from_arg_matches_mut Read more
Source§

impl CommandFactory for ServerConfig

Source§

fn command<'b>() -> Command

Build a Command that can instantiate Self. Read more
Source§

fn command_for_update<'b>() -> Command

Build a Command that can update self. Read more
Source§

impl Default for ServerConfig

Delegates to clap so #[arg(default_value...)] is the single source of truth for every default. Used by the bench crate for struct-literal construction with ..ServerConfig::default().

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl FromArgMatches for ServerConfig

Source§

fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>

Instantiate Self from ArgMatches, parsing the arguments as needed. Read more
Source§

fn from_arg_matches_mut( __clap_arg_matches: &mut ArgMatches, ) -> Result<Self, Error>

Instantiate Self from ArgMatches, parsing the arguments as needed. Read more
Source§

fn update_from_arg_matches( &mut self, __clap_arg_matches: &ArgMatches, ) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

fn update_from_arg_matches_mut( &mut self, __clap_arg_matches: &mut ArgMatches, ) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

impl Parser for ServerConfig

Source§

fn parse() -> Self

Parse from std::env::args_os(), exit on error.
Source§

fn try_parse() -> Result<Self, Error>

Parse from std::env::args_os(), return Err on error.
Source§

fn parse_from<I, T>(itr: I) -> Self
where I: IntoIterator<Item = T>, T: Into<OsString> + Clone,

Parse from iterator, exit on error.
Source§

fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
where I: IntoIterator<Item = T>, T: Into<OsString> + Clone,

Parse from iterator, return Err on error.
Source§

fn update_from<I, T>(&mut self, itr: I)
where I: IntoIterator<Item = T>, T: Into<OsString> + Clone,

Update from iterator, exit on error. Read more
Source§

fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
where I: IntoIterator<Item = T>, T: Into<OsString> + Clone,

Update from iterator, return Err on error.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> OptionalSend for T
where T: Send + ?Sized,

Source§

impl<T> OptionalSync for T
where T: Sync + ?Sized,

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more