query log batching: one transaction per flush interval, not per query
Gates / frontend (push) Successful in 1m18s
Gates / test (push) Successful in 2m46s
Gates / test-aarch64 (push) Successful in 7m33s
Gates / package (push) Successful in 5m34s
Gates / container (push) Successful in 17s
CI / gates (push) Successful in 16m16s
Gates / frontend (push) Successful in 1m8s
Gates / container (push) Successful in 9s
Release / gates (push) Successful in 9m15s
Release / guard (push) Successful in 19s
Gates / test (push) Successful in 1m34s
Gates / test-aarch64 (push) Successful in 6m46s
Gates / package (push) Successful in 39s
Release / publish (push) Failing after 4m7s
Gates / frontend (push) Successful in 1m18s
Gates / test (push) Successful in 2m46s
Gates / test-aarch64 (push) Successful in 7m33s
Gates / package (push) Successful in 5m34s
Gates / container (push) Successful in 17s
CI / gates (push) Successful in 16m16s
Gates / frontend (push) Successful in 1m8s
Gates / container (push) Successful in 9s
Release / gates (push) Successful in 9m15s
Release / guard (push) Successful in 19s
Gates / test (push) Successful in 1m34s
Gates / test-aarch64 (push) Successful in 6m46s
Gates / package (push) Successful in 39s
Release / publish (push) Failing after 4m7s
This commit is contained in:
+26
-19
@@ -921,11 +921,31 @@ fn serve(r: cli.Runner, args: cli.RunArgs) !u8 {
|
||||
// while a task could still touch it.
|
||||
var group: std.Io.Group = .init;
|
||||
|
||||
// The gate every non-essential write consults. Reading it before the
|
||||
// monitor's own task has sampled is safe: a fresh `Monitor` publishes `.ok`
|
||||
// (disk_monitor.zig:63), so nothing is refused for want of a sample.
|
||||
const gate: ?*disk_monitor.Monitor = &monitor;
|
||||
|
||||
// The query-log writer is deliberately *not* in `group`, and starts before
|
||||
// every producer. Inside the group its life would end with the same
|
||||
// `cancel` that stops the producers, and cancellation would race the
|
||||
// queue's close: whichever landed first decided whether the batch the
|
||||
// writer was holding reached the database or was counted as dropped. Given
|
||||
// its own future, it outlives the producers by construction, and the
|
||||
// teardown below can close the queue with nobody left to fill it and then
|
||||
// wait for the writer to finish emptying it.
|
||||
var writer_future = try io.concurrent(
|
||||
logger_mod.Logger.runWriter,
|
||||
.{ &query_logger, io, &querylog_writer_db, gate },
|
||||
);
|
||||
|
||||
// Ruling 4's shutdown order, on the one path every exit from here takes:
|
||||
// the logger sees a closed queue and drains what it holds rather than
|
||||
// losing it to cancellation (ruling 22), then every task stops, and only
|
||||
// then does the final flush run — with no recording task left that could
|
||||
// add a cell after it.
|
||||
// every producer stops and is joined, then the queue closes, then the
|
||||
// writer is awaited — so the last batch is written rather than raced — and
|
||||
// only then does the final history flush run, with no recording task left
|
||||
// that could add a cell after it. A writer the disk gate will not let write
|
||||
// counts its batch as dropped instead of holding the exit open
|
||||
// (`logger.zig`), so this wait always ends.
|
||||
//
|
||||
// A `defer` and not straight-line code after `shutdown.wait`, because a
|
||||
// `concurrent` spawn below can fail with the DNS listeners already
|
||||
@@ -933,25 +953,12 @@ fn serve(r: cli.Runner, args: cli.RunArgs) !u8 {
|
||||
// signal gets. The `querylog_history_db` this flush writes through is
|
||||
// declared above, so its `close` runs after it.
|
||||
defer {
|
||||
query_logger.shutdown(io);
|
||||
group.cancel(io);
|
||||
query_logger.shutdown(io);
|
||||
writer_future.await(io) catch {};
|
||||
history.flushOnce(io, &querylog_history_db, upstream_history_repo.flush);
|
||||
}
|
||||
|
||||
// The gate every non-essential write consults. Reading it before the
|
||||
// monitor's own task has sampled is safe: a fresh `Monitor` publishes `.ok`
|
||||
// (disk_monitor.zig:63), so nothing is refused for want of a sample.
|
||||
const gate: ?*disk_monitor.Monitor = &monitor;
|
||||
|
||||
// The writer starts before the listeners, and that order is the deferred
|
||||
// drain's precondition: a listener that is already accepting queries
|
||||
// enqueues log entries, and `Logger.shutdown` only closes the queue —
|
||||
// someone has to be on the other end to write what it hands over. Spawned
|
||||
// after the listeners, a `concurrent` failure in between would leave those
|
||||
// entries with no consumer and `group.cancel` nothing to drain, which is
|
||||
// exactly the loss the teardown above exists to prevent.
|
||||
try group.concurrent(io, logger_mod.Logger.runWriter, .{ &query_logger, io, &querylog_writer_db, gate });
|
||||
|
||||
if (udp6) |*s| try group.concurrent(io, udp_server.UdpServer.serve, .{ s, io });
|
||||
if (udp4) |*s| try group.concurrent(io, udp_server.UdpServer.serve, .{ s, io });
|
||||
if (tcp6) |*s| try group.concurrent(io, tcp_server.TcpServer.serve, .{ s, io });
|
||||
|
||||
Reference in New Issue
Block a user