//! How much of the window a client asked about the query log can still answer //! for. //! //! Retention deletes old rows and advances a watermark in the same transaction //! (`queries_repo.pruneOlderThan`), so the file knows the oldest instant it is //! complete for. Without that fact on the wire a chart draws a pruned week as a //! week of silence, which is the one reading that is certainly wrong. //! //! Two endpoints carry it — `/api/queries` and `/api/overview` — and they judge //! it against their own effective lower bound: the client's `since` for the //! query log, the period's aligned window start for the overview. const std = @import("std"); const db = @import("../storage/db.zig"); const queries_repo = @import("../storage/repositories/queries_repo.zig"); pub const Coverage = struct { /// True only when the whole requested window is inside what the file still /// holds. A request with no lower bound at all asks about all of history, /// which no file that has ever pruned can promise. complete: bool, /// The oldest instant the file is complete for, unix seconds. available_since: i64, }; pub fn of(available_since: i64, since: ?i64) Coverage { return .{ .complete = if (since) |lower_bound| lower_bound >= available_since else false, .available_since = available_since, }; } /// Reads the watermark for a request that is about to answer. pub fn read(database: *db.Db, since: ?i64) db.Error!Coverage { return of(try queries_repo.availableSince(database), since); } // --------------------------------------------------------------------------- // tests // --------------------------------------------------------------------------- const testing = std.testing; test "a window that starts at or after the watermark is complete" { try testing.expect(of(1000, 1000).complete); try testing.expect(of(1000, 1001).complete); try testing.expect(!of(1000, 999).complete); } test "an unbounded window is never complete" { const unbounded = of(1000, null); try testing.expect(!unbounded.complete); try testing.expectEqual(@as(i64, 1000), unbounded.available_since); }