pub struct Config {
pub node: String,
pub grpc: SocketAddr,
pub http: SocketAddr,
pub data_dir: PathBuf,
pub retention: Duration,
pub max_request_bytes: usize,
pub queue: usize,
pub shards: usize,
pub wal: bool,
pub self_telemetry: bool,
pub telemetry_interval: Duration,
pub alerts: Option<PathBuf>,
}Fields§
§node: StringThis replica’s name. Hashed into the block directory name so that
replicas sharing a volume cannot collide (see mira_core::block).
grpc: SocketAddrWhere OTLP/gRPC listens.
http: SocketAddrWhere OTLP/HTTP, the query API, the MCP endpoint and the web UI listen — one port, because they are one surface over one set of blocks.
data_dir: PathBufThe block directory. It is the whole manifest: no catalogue, no index file, nothing outside it to keep in sync.
retention: DurationHow long a block is kept. Retention is a delete of whole blocks, so the oldest data disappears in block-sized steps rather than row by row.
max_request_bytes: usizeThe largest export either listener will decode. See
receiver::Receivers::max_request_bytes for why it is one number.
queue: usizeHow many exports may be queued for one signal’s flusher before the next
one has to wait for a slot — and is shed with a 503 only if none frees
up within pipeline::ADMIT_WAIT.
The concurrency limit Mira did not used to have. It was a fixed 128 and
a full queue meant an immediate 503, so a wide collector fleet spent
most of its time being told to retry: that sweep shed 93% of exports at
96 connections and landed at a third of the two-connection rate. Waiting
briefly for a slot instead (ADMIT_WAIT, pipeline.rs) took the same
row to nothing shed and double the throughput, and twenty-one
consecutive runs of the whole sweep have refused nothing since. This
knob is the other half — an operator whose fleet is wide can buy queue
depth with memory they have spare.
It buys queueing, not throughput: the flusher drains at the rate it
drains, and a queue deep enough to hide a permanently overloaded node
just moves the shed into a latency tail. Size it to absorb a burst, not
to avoid a 503. Each slot can hold a decoded export, so the worst case is
this times Config::max_request_bytes times three signals resident.
shards: usizeHow many flushers a signal runs, or 0 for “one per two cores”.
One shard per core is the sanctioned unit (architecture.md section 4);
this is only here so the number can be pinned when the machine lies
about its core count. available_parallelism honours cgroup v1 and v2
CPU quotas, so a container with a quota set needs no help here — but
cpu.shares/cpu.weight is a relative weight rather than a quota and
reads as the whole machine, a shared host often sets no quota at all, a
non-Linux container runtime leaves nothing to read, and hyperthreads
count as cores. A 96-core host running Mira on two cores’ worth of any
of those would otherwise start the capped sixteen flushers per signal
and publish sixteen files per seal window. Set it to the cores the
process actually gets, or to 1 to get the pre-0.0.3 behaviour.
Shards split queue, they do not multiply it: the resident worst case
is the same whatever this is. Capped at pipeline::MAX_SHARDS.
wal: boolAcknowledge an export once it is a frame in the write-ahead log, rather than once the block holding it has been published.
The one durability decision Mira does not make for the operator, and it
is not the tuning knob the module docs above rule out: both settings are
correct, they promise different things, and nothing the engine can
measure says which promise a deployment wants. On, the default, is the
log’s: the export survives the process dying, panic = "abort", SIGKILL
and the OOM killer, but not power loss in the last WAL_SYNC_PERIOD,
at a p99 in the microseconds. Off is the block’s — acknowledged means
fsynced and renamed, which survives power loss too, at a p99 of 2.6 s
because that is how long a lightly-loaded block takes to fill.
Read-your-writes holds either way: the open block is queryable (section 4), so a record is visible from the acknowledgement whether or not it has been published yet.
self_telemetry: boolStore this node’s own telemetry in this node, as ordinary metrics.
Mira already knows everything in /api/v1/stats; what it does not do by
default is remember it. On, a task samples those counters every
Config::telemetry_interval and submits them through the metrics
ingest path like any other exporter would — so mira.ingest.rows,
mira.query.latency_ms and the rest become series a chart, an alert rule
or an agent can read with no exporter, no scrape target and no second
system to stand up.
Off by default, because it is not free and the operator should choose to
spend it: the samples are rows, they are subject to
Config::retention like everything else, and a node storing its own
telemetry is a node whose disk usage no longer goes to zero when nothing
is being sent to it.
Self-import is the only destination. Shipping these somewhere else is what an OTLP exporter is for, and Mira is not going to grow a second one pointed at itself.
telemetry_interval: DurationHow often Config::self_telemetry samples this node’s counters.
A sample is one point per series, so this is the resolution of every chart drawn from it and also its cost. The default matches what a collector’s own scrape interval usually is; below a second it is measuring the sampler.
alerts: Option<PathBuf>A KYAML file of alerting rules (crate::alert), or none.
Deployment description rather than a knob, and the same argument as the data directory: what to page on is a thing the engine cannot know. It is a path rather than an inline section for two reasons — a rules list is a list of maps, which this file’s closed-scalar shape refuses on purpose, and rules change on a different cadence to addresses, so they belong in a different file and a different review.
Absent means alerting is off, which is also the coordination mechanism:
N replicas over one block directory would each page, so exactly one
replica gets this key. See crate::alert.
Implementations§
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Config
impl RefUnwindSafe for Config
impl Send for Config
impl Sync for Config
impl Unpin for Config
impl UnsafeUnpin for Config
impl UnwindSafe for Config
Blanket Implementations§
impl<T> Allocation for T
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request§impl<L> LayerExt<L> for L
impl<L> LayerExt<L> for L
§fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
Layered].