overview: one endpoint, live projections and a response cache (m36)
This commit is contained in:
@@ -25,7 +25,7 @@ const log = std.log.scoped(.querylog_schema);
|
||||
/// PLAN §11.3, plus the coverage watermark of milestone 28. Multi-statement
|
||||
/// text — it goes through `db.Db.exec`, never through `prepare`.
|
||||
///
|
||||
/// The trailing INSERT seeds `querylog_meta`, which is part of the schema
|
||||
/// The INSERT seeds `querylog_meta`, which is part of the schema
|
||||
/// rather than a later step: a `query_log` with no watermark beside it cannot
|
||||
/// answer whether an empty result means "no queries" or "no history", and every
|
||||
/// database this program reads from is created by executing this string.
|
||||
@@ -36,6 +36,13 @@ const log = std.log.scoped(.querylog_schema);
|
||||
/// logged in the same second the file was created is not evidence that the
|
||||
/// second is completely covered, and the watermark's whole job is to be
|
||||
/// conservative. From there it only ever advances, in `queries_repo.pruneOlderThan`.
|
||||
///
|
||||
/// The four `bucket_*` tables are the Overview projections (milestone 36), on a
|
||||
/// 30-minute grain that divides every serving width the API offers. They carry
|
||||
/// no history of their own: they are born with the file and maintained in the
|
||||
/// same transaction as every insert and every prune, so SQLite's transaction is
|
||||
/// the only coherence mechanism there is. There is no backfill path — a file
|
||||
/// whose projections could disagree with its rows cannot exist.
|
||||
pub const ddl: [:0]const u8 =
|
||||
\\CREATE TABLE domains (
|
||||
\\ id INTEGER PRIMARY KEY,
|
||||
@@ -78,6 +85,40 @@ pub const ddl: [:0]const u8 =
|
||||
\\);
|
||||
\\INSERT INTO querylog_meta (id, created_at, available_since)
|
||||
\\VALUES (1, unixepoch(), unixepoch() + 1);
|
||||
\\
|
||||
\\CREATE TABLE bucket_totals (
|
||||
\\ bucket INTEGER PRIMARY KEY,
|
||||
\\ queries INTEGER NOT NULL,
|
||||
\\ blocked INTEGER NOT NULL,
|
||||
\\ cached INTEGER NOT NULL,
|
||||
\\ rt_sum INTEGER NOT NULL, -- sum(response_time_us) over timed rows
|
||||
\\ rt_count INTEGER NOT NULL -- count(response_time_us)
|
||||
\\) WITHOUT ROWID;
|
||||
\\
|
||||
\\CREATE TABLE bucket_clients (
|
||||
\\ bucket INTEGER NOT NULL,
|
||||
\\ client_ip TEXT NOT NULL,
|
||||
\\ queries INTEGER NOT NULL,
|
||||
\\ PRIMARY KEY (bucket, client_ip)
|
||||
\\) WITHOUT ROWID;
|
||||
\\
|
||||
\\CREATE TABLE bucket_types (
|
||||
\\ bucket INTEGER NOT NULL,
|
||||
\\ qtype INTEGER NOT NULL, -- -1 encodes a NULL qtype, losslessly
|
||||
\\ count INTEGER NOT NULL,
|
||||
\\ PRIMARY KEY (bucket, qtype)
|
||||
\\) WITHOUT ROWID;
|
||||
\\
|
||||
\\CREATE TABLE bucket_routes (
|
||||
\\ bucket INTEGER NOT NULL,
|
||||
\\ route_kind TEXT NOT NULL,
|
||||
\\ source_present INTEGER NOT NULL, -- 0: source NULL; 1: source = source_text
|
||||
\\ source_text TEXT NOT NULL, -- '' when source_present = 0
|
||||
\\ count INTEGER NOT NULL,
|
||||
\\ PRIMARY KEY (bucket, route_kind, source_present, source_text),
|
||||
\\ CHECK (source_present IN (0, 1)),
|
||||
\\ CHECK (source_present = 1 OR source_text = '')
|
||||
\\) WITHOUT ROWID;
|
||||
;
|
||||
|
||||
/// The fingerprint of an arbitrary DDL text. `tools/cut.zig` calls this at
|
||||
@@ -324,13 +365,23 @@ test "ddl creates the query-log tables and every index" {
|
||||
try database.exec(ddl);
|
||||
|
||||
try testing.expectEqual(
|
||||
@as(i64, 3),
|
||||
@as(i64, 7),
|
||||
try database.queryInt("SELECT count(*) FROM sqlite_schema WHERE type='table'"),
|
||||
);
|
||||
// The three explicit indexes plus `domains.domain`'s autoindex, and
|
||||
// nothing else: the four projection tables are WITHOUT ROWID, so each
|
||||
// one's PRIMARY KEY *is* its storage rather than a second b-tree to keep
|
||||
// in step on every insert.
|
||||
try testing.expectEqual(
|
||||
@as(i64, 4),
|
||||
try database.queryInt("SELECT count(*) FROM sqlite_schema WHERE type='index'"),
|
||||
);
|
||||
const objects = [_][]const u8{
|
||||
"domains", "query_log",
|
||||
"idx_query_log_ts", "idx_query_log_client",
|
||||
"idx_query_log_domain", "querylog_meta",
|
||||
"bucket_totals", "bucket_clients",
|
||||
"bucket_types", "bucket_routes",
|
||||
};
|
||||
for (objects) |name| {
|
||||
var stmt = try database.prepare("SELECT count(*) FROM sqlite_schema WHERE name = ?1");
|
||||
|
||||
Reference in New Issue
Block a user