Expand description
The ingest path.
One bounded tokio::sync::mpsc channel per shard feeding one flusher task.
The brief called for a lock-free ring buffer. That is the right structure
when items are ~150ns order structs arriving millions per second; here an
item is a whole export request costing 10^5–10^6 ns to decode and encode, and
the realistic arrival rate is 10^2–10^4 per second. At that ratio the queue
is never the bottleneck, and a bounded async channel buys the thing a
lock-free queue cannot: send().await applies real backpressure that
propagates out as HTTP/2 flow control to the exporter, instead of either
spinning or dropping. If a queue ever shows up in a profile, this is one type
to change.
Flush is spawn_blocking: it fsyncs.
Where the acknowledgement happens is Config::wal’s decision, and it is
the only one in this file. Without a log the export is acknowledged after
the block directory rename is durable, because OTLP’s retryable status set
covers exports in flight at a crash — acking earlier is the one window where
data is lost with the client believing it was stored. That costs a whole
max_block_age at the tail, which is section 11’s 2.6 s p99. With a log the frame
is the durable record, the publish is a background reorganisation of data
that is already safe, and the ack costs a write(2). Everything else here —
the queue, the carry, the failure contract — is identical either way.
Structs§
- Config
- Engine configuration.
- Flushers
- The handle over one signal’s flusher shards: what a single
JoinHandlemeant when there was one of them, kept true now that there are several. - Ingest
- The write handle for one signal.
Ris that signal’s OTLP export request. - Job 🔒
- Open
Slot - Every shard of one signal’s open blocks, asked together.
- Rejects
- What each signal has refused, published and been stuck on, since start.
- Shard 🔒
- Where the read path asks one flusher shard for a readable copy of its open block (section 4), and where the last copy it produced is cached.
Enums§
- Rejected
- Why an export could not be admitted. None of these is a partial success: OTLP forbids the client from retrying a partial success, so reporting overload that way permanently destroys the data and blames the sender.
Constants§
- ADMIT_
WAIT 🔒 - How long an export waits for room in the queue before it is shed.
- MAX_
SHARDS - The most flusher shards one signal will run.
- MIN_
FREE 🔒 - Free space below which retention stops waiting for the TTL.
- SIGNALS
- Every signal that has an on-disk directory. Retention sweeps all of them;
block::scantreats a missing one as empty, so listing a signal before its encoder exists is harmless. - UNREADY_
AFTER - How long a signal has to be unable to store anything before this node calls itself unready.
- WAL_
SYNC_ PERIOD - How long an acknowledged export can sit in the page cache before it is on the device.
Statics§
Functions§
- answer 🔒
- Answer every reader waiting on the open block, if there is nothing left queued ahead of them (section 4).
- dir_
bytes 🔒 - The size of one block, as the filesystem sees it. Best-effort: a block being unlinked by another replica mid-walk is worth a slightly low counter, not an error path on the flush.
- flusher 🔒
- now_
secs 🔒 - Wall-clock seconds. Only ever used for rate limits and for ages an operator reads; block timestamps come from the data, never from this clock.
- once_
a_ 🔒second - True at most once per wall-clock second per gate, for whichever caller gets there first.
- reclaim 🔒
- Drop the oldest blocks, across every signal, until the volume is back above
min_free. Returns what was unlinked, oldest first. - rejects_
for 🔒 - retention 🔒
- shard_
count - How many flusher shards to run, given the configured value and what the machine reports.
- spawn
- Start one signal’s ingest pipeline. Returns the handle its receivers push into. Each signal gets its own channels, flusher tasks and block sequences, so a slow flush on one cannot stall another.
- spawn_
retention - One sweep for all signals, not one per signal: retention is IO against the directory tree, and three tasks waking on the same minute boundary to unlink from the same volume is contention for nothing.
- stall_
of 🔒 - Seconds
rhas been unable to store an export, if that is long enough to be worth acting on. Split out fromstalledso the threshold is testable without writing to a process-wide static that three live flushers also own. - stalled
- The first signal this node has been unable to store for longer than
UNREADY_AFTER, and for how many seconds.Nonemeans every signal is either healthy or has only just started failing. - wal_
maintenance 🔒 - Sync the log to the device, and drop the segments every signal has published past.
- wal_
sweep 🔒 - One pass of the above. Separate from the loop so a test can drive both kinds of tick without waiting out the sixty seconds of real time between them.