pub struct ServerConfig {Show 46 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 max_orders_per_account: u32,
pub max_orders_per_second: u32,
pub max_orders_burst: u32,
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 dpdk_eal_args: String,
pub dpdk_ports: Vec<u16>,
pub dpdk_ip: String,
pub dpdk_prefix_len: u8,
pub dpdk_gateway: Option<String>,
pub dpdk_peer_ip: Option<String>,
pub dpdk_gateway_mac: Option<String>,
pub dpdk_peer_mac: Option<String>,
pub dpdk_mtu: usize,
pub dpdk_vlan: Option<u16>,
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: SocketAddrAddress to bind the TCP listener.
journal: PathBufPath to the journal file for durable event sourcing.
snapshot: Option<PathBuf>Path to a snapshot file for faster recovery.
cores: PipelineCoresWhere 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: u64Group commit coalescing delay in microseconds. Keep at 0 for TCP.
heartbeat_interval_secs: u64Heartbeat 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: u64Connection timeout in seconds. The server disconnects clients that have not sent any data within this window. Set to 0 to disable.
max_connections: u64Maximum number of concurrent authenticated connections. New connections are rejected (closed immediately) when this limit is reached. 0 means unlimited. Prevents fd/memory exhaustion (SEC-02).
Path to the authorized keys file for Ed25519 challenge-response
authentication. Every connection must authenticate before trading.
Required for primary mode; ignored in replica mode (–replica-of).
See AuthorizedKeys for file format.
max_journal_mib: u64Maximum 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: JournalStagingModeHow 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’sfdatasyncnever forces the filesystem log. It costs one extra pass over every segment in device bandwidth.allocatereserves 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.
max_orders_per_account: u32Maximum number of open orders (resting limits + pending stops, across
all instruments) per account. New submissions are rejected with
ExceedsMaxOpenOrders once an account hits this cap. 0 means
unlimited. Bounds the per-account contribution to engine memory
(SEC-03). Must be set to the same value on the primary and every
replica — the cap shapes Rejected reports, so a mismatch causes
replay divergence.
max_orders_per_second: u32Per-account sustained order-submission rate (orders/sec). Token
bucket refills at this rate; clients exceeding it (after burning
the burst) are rejected with ExceedsOrderRate. 0 disables the
limiter. Prevents a single client from monopolizing matching
throughput (SEC-04). Must match across primary and every replica
— same determinism caveat as --max-orders-per-account. Default
(1000/s) is a conservative mid-tier ceiling: comfortable for
algorithmic and retail flow, but tight for active market-makers
who routinely re-quote past 1k/s on liquid instruments. Operators
running with significant MM presence should raise this (and the
burst) to match their book’s quote-update profile.
max_orders_burst: u32Per-account burst capacity (max consecutive orders allowed after a
quiet period). Paired with --max-orders-per-second. 0 disables
the limiter. Default (5000) lets normal trading bursts through —
e.g. a market open or a strategy initialization batch — without
false positives, while still capping the absolute spike a single
account can produce.
replication_bind: Option<SocketAddr>Address to listen for replica connections (enables synchronous replication).
Mutually exclusive with --standalone and --replica-of.
standalone: boolDisable 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
with permission replication.
replication_batch_size: usizeMaximum 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: usizeMaximum 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: u64Replication 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: usizeNumber 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: usizeNumber 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: AckPolicyAck policy: which copies of an event must exist before its response is released. One of:
diskpersisted>=1. One fsynced copy, on whichever node’s disk confirms first; required with--standalone. Dev/staging deployments.ramin_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 trading deployments. Saves ~50–80 µs per fill vstwo-disks.two-diskspersisted>=2. Two fsynced copies before the client ack. Zero RAM-only window; the gate stalls when no replica is connected. Compliance-driven venues.
--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.
dpdk_eal_args: StringDPDK EAL arguments (space-separated). Example: –dpdk-eal-args=“-l 0-7 –huge-dir /dev/hugepages”.
Passed directly to rte_eal_init. Only used when compiled with –features dpdk.
The = is required: the value itself starts with a dash, and requiring
the joined form keeps a forgotten value from silently swallowing the
next flag as the EAL string.
dpdk_ports: Vec<u16>DPDK port IDs (comma-separated). For LACP bonds, pass both VF ports (e.g., “0,1”) so traffic arriving on either bond member is received.
dpdk_ip: StringIPv4 address for the DPDK interface (e.g., “10.0.0.1”).
dpdk_prefix_len: u8IPv4 prefix length for the DPDK interface. Default: 24.
dpdk_gateway: Option<String>IPv4 gateway for the DPDK interface (optional, needed for cross-subnet traffic).
dpdk_peer_ip: Option<String>Peer IPv4 used for the bifurcated rte_flow steering rule. When
set, the DPDK port opens in isolated mode and only IPv4 packets
sourced from this address are delivered into DPDK queue 0 —
everything else stays with the kernel netdev. Required for L3
setups that share the public NIC with the kernel (SSH, etc.).
dpdk_gateway_mac: Option<String>Gateway MAC (aa:bb:cc:dd:ee:ff) seeded into smoltcp for the
--dpdk-gateway IP. Required in L3 bifurcated mode because the
gateway’s ARP reply would not match the steering rule and would
be eaten by the kernel. Source from ip neigh on the host.
dpdk_peer_mac: Option<String>MAC (aa:bb:cc:dd:ee:ff) of the primary named by --replica-of,
seeded into smoltcp so the replica’s first frame is addressed
before any ARP. Required on ports that keep a real hardware MAC —
an mlx5 in bifurcated mode shares the kernel netdev’s — where the
SR-IOV 02:00:<ip> fallback is wrong and the replica would spin
on connect with no error. Read it from
/sys/class/net/<iface>/address on the primary, or from
DPDK_MAC in /etc/melin-dpdk.conf. Ignored on a primary, which
learns peer MACs from inbound frames.
dpdk_mtu: usizeMTU for the DPDK interface. Use 9000 for jumbo frames (6x fewer TCP segments). Requires switch and PF MTU to be set accordingly.
dpdk_vlan: Option<u16>VLAN ID for hardware VLAN strip/insert. Required in dedicated NIC mode (dpdk-setup-dedicated.sh) where the kernel doesn’t handle VLAN tags. Not needed for SR-IOV mode (the PF handles VLAN tagging).
event_bind: Option<SocketAddr>Address for the output event publisher. Subscribers connect here to receive a real-time stream of all execution events (market data, fills, cancellations). Ed25519 auth required (ReadOnly or above). 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: u64Interval 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: u64Cadence in milliseconds for the engine-internal scheduler tick.
The ingress thread (io_uring reader or DPDK poll thread) publishes
a JournalEvent::Tick { now_ns } at this interval so time-driven
tasks (GTD expiry, volatility halts, session transitions) fire 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 its
scheduler 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 quiet-market scheduler firings within
a quarter-second of their deadline at a cost of ~4 events/sec of
journal traffic.
no_mlock: boolDisable 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 exchange 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: boolAct 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
impl ServerConfig
Sourcepub fn group_commit_delay(&self) -> Duration
pub fn group_commit_delay(&self) -> Duration
Group commit delay as a Duration.
Sourcepub fn heartbeat_interval(&self) -> Option<Duration>
pub fn heartbeat_interval(&self) -> Option<Duration>
Heartbeat interval as a Duration. Returns None if disabled (0).
Sourcepub fn connection_timeout(&self) -> Option<Duration>
pub fn connection_timeout(&self) -> Option<Duration>
Connection timeout as a Duration. Returns None if disabled (0).
Sourcepub fn shadow_snapshot_path(&self) -> PathBuf
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.
Sourcepub fn tick_interval(&self) -> Option<Duration>
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).
Sourcepub fn resolved_cores(
&self,
reader: ReaderThread,
) -> Result<PipelineCores, String>
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
impl Args for ServerConfig
Source§fn augment_args<'b>(__clap_app: Command) -> Command
fn augment_args<'b>(__clap_app: Command) -> Command
Source§fn augment_args_for_update<'b>(__clap_app: Command) -> Command
fn augment_args_for_update<'b>(__clap_app: Command) -> Command
Command so it can instantiate self via
FromArgMatches::update_from_arg_matches_mut Read moreSource§impl CommandFactory for ServerConfig
impl CommandFactory for ServerConfig
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().
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§impl FromArgMatches for ServerConfig
impl FromArgMatches for ServerConfig
Source§fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>
fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>
Source§fn from_arg_matches_mut(
__clap_arg_matches: &mut ArgMatches,
) -> Result<Self, Error>
fn from_arg_matches_mut( __clap_arg_matches: &mut ArgMatches, ) -> Result<Self, Error>
Source§fn update_from_arg_matches(
&mut self,
__clap_arg_matches: &ArgMatches,
) -> Result<(), Error>
fn update_from_arg_matches( &mut self, __clap_arg_matches: &ArgMatches, ) -> Result<(), Error>
ArgMatches to self.Source§fn update_from_arg_matches_mut(
&mut self,
__clap_arg_matches: &mut ArgMatches,
) -> Result<(), Error>
fn update_from_arg_matches_mut( &mut self, __clap_arg_matches: &mut ArgMatches, ) -> Result<(), Error>
ArgMatches to self.