milestone 8: web server, rest api, sse, auth, metrics and static assets
This commit is contained in:
+52
-7
@@ -7,9 +7,11 @@
|
||||
//! the moment the query finishes. That is the whole reason this file has fixed
|
||||
//! buffers instead of slices.
|
||||
//!
|
||||
//! The privacy transforms of §11.4 run inside `log`, before the entry is
|
||||
//! enqueued, so nothing downstream — the database now, Phase 8's event stream
|
||||
//! later — can observe a value the operator asked to hide.
|
||||
//! The privacy transforms of §11.4 run in `transformed`, before the entry is
|
||||
//! enqueued, so nothing downstream — the database or the event stream — can
|
||||
//! observe a value the operator asked to hide. `log` is the two halves in
|
||||
//! order; `QuerySink` calls them separately so both of its consumers see the
|
||||
//! one transformed entry.
|
||||
//!
|
||||
//! Log rows are expendable. A full queue drops the oldest unflushed entry, a
|
||||
//! failed batch is dropped whole, and a disk that crossed the critical
|
||||
@@ -191,10 +193,23 @@ pub const Logger = struct {
|
||||
/// Applies the privacy transforms and enqueues without ever blocking the
|
||||
/// query path. A full queue loses its oldest unflushed entry (§11.4).
|
||||
pub fn log(self: *Logger, io: std.Io, entry: Entry) void {
|
||||
var transformed = entry;
|
||||
if (self.cfg.hide_domains) transformed.setDomain(hidden_marker);
|
||||
if (self.cfg.hide_client_ips) transformed.setClientIp(hidden_marker);
|
||||
self.enqueue(io, transformed);
|
||||
self.logTransformed(io, self.transformed(entry));
|
||||
}
|
||||
|
||||
/// The §11.4 privacy transforms, on their own. `QuerySink` runs them once
|
||||
/// and hands the result to every consumer, so nothing downstream — the
|
||||
/// database or the event stream — can observe a value the operator asked
|
||||
/// to hide.
|
||||
pub fn transformed(self: *const Logger, entry: Entry) Entry {
|
||||
var out = entry;
|
||||
if (self.cfg.hide_domains) out.setDomain(hidden_marker);
|
||||
if (self.cfg.hide_client_ips) out.setClientIp(hidden_marker);
|
||||
return out;
|
||||
}
|
||||
|
||||
/// `log` without the transforms, for a caller that already applied them.
|
||||
pub fn logTransformed(self: *Logger, io: std.Io, entry: Entry) void {
|
||||
self.enqueue(io, entry);
|
||||
}
|
||||
|
||||
/// Retries until the put succeeds, and each failed attempt drops exactly
|
||||
@@ -556,6 +571,36 @@ test "log hides only the field its switch names" {
|
||||
try testing.expectEqualStrings("192.0.2.10", untouched.clientIp());
|
||||
}
|
||||
|
||||
test "the split halves reproduce log byte for byte" {
|
||||
var threaded: std.Io.Threaded = .init(testing.allocator, .{});
|
||||
defer threaded.deinit();
|
||||
const io = threaded.io();
|
||||
|
||||
const configs = [_]model.Logging{
|
||||
.{},
|
||||
.{ .hide_domains = true },
|
||||
.{ .hide_client_ips = true },
|
||||
.{ .hide_domains = true, .hide_client_ips = true },
|
||||
};
|
||||
|
||||
for (configs) |cfg| {
|
||||
var buf: [4]Entry = undefined;
|
||||
var logger: Logger = .init(cfg, &buf);
|
||||
const source = sampleEntry(100, "tracker.example");
|
||||
|
||||
logger.log(io, source);
|
||||
logger.logTransformed(io, logger.transformed(source));
|
||||
|
||||
const from_log = try logger.queue.getOne(io);
|
||||
const from_halves = try logger.queue.getOne(io);
|
||||
try testing.expectEqualSlices(
|
||||
u8,
|
||||
std.mem.asBytes(&from_log),
|
||||
std.mem.asBytes(&from_halves),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
test "a full queue drops the oldest entry and counts it" {
|
||||
var threaded: std.Io.Threaded = .init(testing.allocator, .{});
|
||||
defer threaded.deinit();
|
||||
|
||||
Reference in New Issue
Block a user