storage: version querylog.db and migrate it in place, never reset a healthy file

querylog.db carries a schema version; migrations run at startup as one transaction after a vacuumed 0600 backup, and every failure refuses startup (exit 2, no systemd restart loop) instead of starting empty. corruption is the only automatic recreate left. the cut gate now requires a fixture-proven migration or an explicit versioned break with restore instructions, and locks shipped migration files and fixtures byte-for-byte.
This commit is contained in:
2026-08-28 17:56:19 +02:00
parent dd5a9f6ab8
commit 272655f60c
25 changed files with 4312 additions and 176 deletions
+88
View File
@@ -0,0 +1,88 @@
-- FROZEN FIXTURE. Representative content for a querylog.db at schema version 1,
-- loaded on top of `querylog-v1-schema.sql`.
--
-- IMMUTABLE once released, for the same reason as its schema half: the release
-- gate byte-compares it against the previous tag. A future schema version ships
-- a NEW pair rather than editing this one.
--
-- The rows are chosen to be hard on a migration rather than realistic: every
-- `route_kind`, the NULL variants of `qtype`, `cache_hit`, `response_time_us`,
-- `upstream` and `forward_zone`, a non-IN qclass with a non-zero rcode, the
-- group and source id/name pairs, `cname_target`/`safe_search_target`, an
-- `available_since` that has been advanced away from its DDL default, and
-- timestamps that straddle two 30-minute projection buckets.
--
-- The `bucket_*` rows below are the recomputation of the raw rows, transcribed
-- from the same SQL `queries_repo`'s coherence oracle recomputes with. A
-- fixture-validity test runs that oracle over this file BEFORE any migration,
-- so an incoherent transcription fails on its own rather than as a migration
-- bug.
UPDATE querylog_meta SET created_at = 1699998000, available_since = 1699998600 WHERE id = 1;
INSERT INTO domains (id, domain) VALUES
(1, 'ads.example'),
(2, 'news.example'),
(3, 'chat.example'),
(4, 'printer.lan'),
(5, 'nas.lan'),
(6, 'cdn.example');
INSERT INTO query_log (
id, timestamp, domain_id, client_ip, qtype, blocked, response_time_us,
cache_hit, upstream, qclass, rcode, group_id, group_name, policy_action,
policy_reason, matched, source_id, source_name, cname_target,
safe_search_target, route_kind, forward_zone
) VALUES
(1, 1699999260, 1, '10.0.0.1', 1, 1, NULL, 0, NULL, 1, 0, 7, 'kids',
'block', 'blocklist_domain', 'ads.example', 3, 'stevenblack', NULL, NULL,
'blocked', NULL),
(2, 1699999320, 2, '10.0.0.1', 28, 0, 1500, 0, '9.9.9.9:853', 1, 0, NULL,
NULL, 'allow', 'no_match', NULL, NULL, NULL, NULL, NULL, 'upstream', NULL),
(3, 1699999380, 2, '10.0.0.2', 1, 0, 90, 1, NULL, 1, 0, NULL, NULL,
'allow', 'no_match', NULL, NULL, NULL, NULL, NULL, 'cache', NULL),
(4, 1699999440, 3, '10.0.0.2', NULL, 0, NULL, NULL, NULL, 3, 4, NULL, NULL,
'not_evaluated', 'non_in_class', NULL, NULL, NULL, NULL, NULL, 'rejected',
NULL),
(5, 1699999500, 4, '10.0.0.3', 1, 0, 200, 0, NULL, 1, 0, NULL, NULL,
'not_evaluated', 'local_record', NULL, NULL, NULL, NULL, NULL, 'local',
NULL),
(6, 1700000700, 5, '10.0.0.3', 15, 0, 3400, 0, NULL, 1, 0, NULL, NULL,
'not_evaluated', 'forward_zone', NULL, NULL, NULL, NULL, NULL,
'forward_zone', 'lan.example'),
(7, 1700001060, 1, '10.0.0.1', 1, 1, NULL, 0, NULL, 1, 0, 7, 'kids',
'block', 'blocklist_wildcard', '*.ads.example', 3, 'stevenblack', NULL,
NULL, 'blocked', NULL),
(8, 1700001120, 6, '10.0.0.4', 65, 0, 2500, 1, '1.1.1.1:853', 1, 0, NULL,
NULL, 'allow', 'no_match', NULL, NULL, NULL, 'edge.cdn.example',
'forcesafesearch.example', 'upstream', NULL);
INSERT INTO bucket_totals (bucket, queries, blocked, cached, rt_sum, rt_count) VALUES
(1699999200, 6, 1, 1, 5190, 4),
(1700001000, 2, 1, 1, 2500, 1);
INSERT INTO bucket_clients (bucket, client_ip, queries) VALUES
(1699999200, '10.0.0.1', 2),
(1699999200, '10.0.0.2', 2),
(1699999200, '10.0.0.3', 2),
(1700001000, '10.0.0.1', 1),
(1700001000, '10.0.0.4', 1);
-- qtype -1 is the lossless encoding of the NULL qtype on row 4.
INSERT INTO bucket_types (bucket, qtype, count) VALUES
(1699999200, -1, 1),
(1699999200, 1, 3),
(1699999200, 15, 1),
(1699999200, 28, 1),
(1700001000, 1, 1),
(1700001000, 65, 1);
INSERT INTO bucket_routes (bucket, route_kind, source_present, source_text, count) VALUES
(1699999200, 'blocked', 0, '', 1),
(1699999200, 'cache', 0, '', 1),
(1699999200, 'forward_zone', 1, 'lan.example', 1),
(1699999200, 'local', 0, '', 1),
(1699999200, 'rejected', 0, '', 1),
(1699999200, 'upstream', 1, '9.9.9.9:853', 1),
(1700001000, 'blocked', 0, '', 1),
(1700001000, 'upstream', 1, '1.1.1.1:853', 1);