milestone 7: serving pipeline, client tracking, pause and lifecycle
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
//! allocates nothing. The pool is sized once in `bind` and never grows.
|
||||
|
||||
const std = @import("std");
|
||||
const address = @import("../platform/address.zig");
|
||||
const handler = @import("handler.zig");
|
||||
const transport = @import("../upstream/transport.zig");
|
||||
|
||||
@@ -75,10 +76,14 @@ pub const UdpServer = struct {
|
||||
/// The datagram actually sent stays bounded by `udpLimit` inside the
|
||||
/// handler, so nothing larger than 4096 bytes leaves this socket.
|
||||
///
|
||||
/// Cost: 4096 + 65535 ≈ 68 KiB per slot, so the default 64 slots hold
|
||||
/// ≈ 4.3 MiB. The PLAN §18 budget is 100 MB with ~1M blocked domains,
|
||||
/// so this pool takes about 4% of it.
|
||||
/// Cost: 4096 + 65535 + the scratch below ≈ 74 KiB per slot, so the
|
||||
/// default 64 slots hold ≈ 4.6 MiB. The PLAN §18 budget is 100 MB with
|
||||
/// ~1M blocked domains, so this pool takes about 5% of it.
|
||||
reply: [transport.max_message_len]u8,
|
||||
/// The handler's per-query working memory. It belongs to the slot so
|
||||
/// that answering a datagram still allocates nothing, and one slot
|
||||
/// serves one query at a time.
|
||||
scratch: handler.Scratch,
|
||||
from: std.Io.net.IpAddress,
|
||||
len: usize,
|
||||
/// Guarded by `UdpServer.mutex`.
|
||||
@@ -90,7 +95,7 @@ pub const UdpServer = struct {
|
||||
pub fn bind(
|
||||
gpa: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
address: std.Io.net.IpAddress,
|
||||
bind_address: std.Io.net.IpAddress,
|
||||
h: *handler.Handler,
|
||||
options: Options,
|
||||
) BindError!UdpServer {
|
||||
@@ -100,7 +105,7 @@ pub const UdpServer = struct {
|
||||
errdefer gpa.free(slots);
|
||||
for (slots) |*slot| slot.in_use = false;
|
||||
|
||||
const local = address;
|
||||
const local = bind_address;
|
||||
const socket = try local.bind(io, .{ .mode = .dgram });
|
||||
|
||||
return .{
|
||||
@@ -204,7 +209,15 @@ pub const UdpServer = struct {
|
||||
const slot = &self.slots[index];
|
||||
defer self.release(io, index);
|
||||
|
||||
switch (self.handler.handle(io, .udp, slot.query[0..slot.len], &slot.reply)) {
|
||||
const outcome = self.handler.handle(
|
||||
io,
|
||||
.udp,
|
||||
address.NetAddress.fromIp(slot.from),
|
||||
slot.query[0..slot.len],
|
||||
&slot.reply,
|
||||
&slot.scratch,
|
||||
);
|
||||
switch (outcome) {
|
||||
.drop => bump(&self.stats.dropped_handler),
|
||||
.reply => |bytes| self.socket.send(io, &slot.from, bytes) catch |err| {
|
||||
bump(&self.stats.send_errors);
|
||||
@@ -283,8 +296,8 @@ test "a slot's reply buffer holds a whole DNS message" {
|
||||
}
|
||||
|
||||
test "the default slot pool stays inside the memory budget" {
|
||||
// 4096 + 65535 ≈ 68 KiB per slot; 64 slots ≈ 4.3 MiB, against the 100 MB
|
||||
// of PLAN §18.
|
||||
// 4096 + 65535 + scratch ≈ 74 KiB per slot; 64 slots ≈ 4.6 MiB, against the
|
||||
// 100 MB of PLAN §18.
|
||||
const options: Options = .{};
|
||||
const pool_bytes = @sizeOf(UdpServer.Slot) * @as(usize, options.max_in_flight);
|
||||
try testing.expect(pool_bytes < 8 * 1024 * 1024);
|
||||
|
||||
Reference in New Issue
Block a user