In-memory insert batcher — the fire-and-forget, lossy counterpart to the outcome
group-commit GenDurable.Flusher.
GenDurable.insert_async/2 buffers a row's params here and returns :ok
immediately, without waiting for the write. The batcher coalesces buffered rows
into one GenDurable.Queries.insert_all/2 on a size/time trigger and then pokes the
affected queues (out of band). The opposite durability contract to the Flusher:
the caller does not block on the write, so a row still buffered when the VM dies
abruptly is lost. For throughput workloads where an occasional dropped insert is
acceptable; at-least-once work uses the synchronous insert/2.
Two triggers, whichever fires first (same shape as the Flusher):
max_batch(default 1000) — flush as soon as this many rows are buffered;max_delay_ms(default 100) — flush this long after the first buffered row, bounding the loss/latency window under light load.
Under load the batch auto-grows: while a flush runs, the mailbox fills, so the next flush is bigger — the single serialization point does not become a linear bottleneck.
Backpressure. The pending depth is an :atomics counter, bumped by insert_async
before it casts and decremented here per flush. When the depth reaches max_buffer,
insert_async falls back to a synchronous, durable insert instead of buffering — so
an overwhelmed batcher degrades to durable writes rather than growing without bound.
On graceful shutdown the batcher drains its buffer (a final insert_all), so a
clean stop loses nothing; the loss window is only an abrupt VM death with rows still
buffered.
Summary
Functions
Returns a specification to start this module under a supervisor.
Buffer one row's params (async). The atomics depth was already bumped by the caller.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
Buffer one row's params (async). The atomics depth was already bumped by the caller.