-- Migration 001: Initial schema -- Creates all tables for the pixa caching image proxy -- Source images, one row per source URL -- Files stored at: cache/sources///, where source_key -- is the SHA-256 of the source's host, path and query CREATE TABLE IF NOT EXISTS source_content ( source_key TEXT PRIMARY KEY, content_type TEXT NOT NULL, size_bytes INTEGER NOT NULL, last_accessed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_source_content_last_accessed ON source_content(last_accessed_at); -- Processed variant blobs -- Files stored at: cache/variants///. Tracked here -- (like source content blobs above) so total cache usage is known -- without a directory scan, and so LRU eviction has a timestamp to order -- on. A cache hit does not read this table: it takes the content type -- from the file's first bytes, and its last_accessed_at is written -- later, in a batch. CREATE TABLE IF NOT EXISTS variant_content ( cache_key TEXT PRIMARY KEY, size_bytes INTEGER NOT NULL, last_accessed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_variant_content_last_accessed ON variant_content(last_accessed_at); -- Total cache usage: the sum of size_bytes over source_content and -- variant_content, kept by the triggers below in the same statement -- that adds, removes or resizes a row, so eviction reads this one row -- instead of summing both tables. Each trigger also adds one to -- change_count; the reconciliation pass, which sums both tables a page -- at a time, corrects total_size_bytes only if change_count did not -- move while it summed. CREATE TABLE IF NOT EXISTS cache_usage ( id INTEGER PRIMARY KEY CHECK (id = 1), total_size_bytes INTEGER NOT NULL DEFAULT 0, change_count INTEGER NOT NULL DEFAULT 0 ); INSERT OR IGNORE INTO cache_usage (id) VALUES (1); CREATE TRIGGER IF NOT EXISTS source_content_usage_insert AFTER INSERT ON source_content BEGIN UPDATE cache_usage SET total_size_bytes = total_size_bytes + NEW.size_bytes, change_count = change_count + 1 WHERE id = 1; END; CREATE TRIGGER IF NOT EXISTS source_content_usage_delete AFTER DELETE ON source_content BEGIN UPDATE cache_usage SET total_size_bytes = total_size_bytes - OLD.size_bytes, change_count = change_count + 1 WHERE id = 1; END; CREATE TRIGGER IF NOT EXISTS source_content_usage_update AFTER UPDATE OF size_bytes ON source_content BEGIN UPDATE cache_usage SET total_size_bytes = total_size_bytes - OLD.size_bytes + NEW.size_bytes, change_count = change_count + 1 WHERE id = 1; END; CREATE TRIGGER IF NOT EXISTS variant_content_usage_insert AFTER INSERT ON variant_content BEGIN UPDATE cache_usage SET total_size_bytes = total_size_bytes + NEW.size_bytes, change_count = change_count + 1 WHERE id = 1; END; CREATE TRIGGER IF NOT EXISTS variant_content_usage_delete AFTER DELETE ON variant_content BEGIN UPDATE cache_usage SET total_size_bytes = total_size_bytes - OLD.size_bytes, change_count = change_count + 1 WHERE id = 1; END; CREATE TRIGGER IF NOT EXISTS variant_content_usage_update AFTER UPDATE OF size_bytes ON variant_content BEGIN UPDATE cache_usage SET total_size_bytes = total_size_bytes - OLD.size_bytes + NEW.size_bytes, change_count = change_count + 1 WHERE id = 1; END; -- Negative cache: source URLs whose fetch failed with an upstream error -- status, or whose first bytes did not match their Content-Type. pixa does -- not fetch such a URL again until its row expires. CREATE TABLE IF NOT EXISTS negative_cache ( id INTEGER PRIMARY KEY AUTOINCREMENT, source_host TEXT NOT NULL, source_path TEXT NOT NULL, source_query TEXT NOT NULL DEFAULT '', expires_at DATETIME NOT NULL, UNIQUE(source_host, source_path, source_query) ); CREATE INDEX IF NOT EXISTS idx_negative_cache_expires ON negative_cache(expires_at); -- Cache statistics for monitoring CREATE TABLE IF NOT EXISTS cache_stats ( id INTEGER PRIMARY KEY CHECK (id = 1), hit_count INTEGER NOT NULL DEFAULT 0, miss_count INTEGER NOT NULL DEFAULT 0, upstream_fetch_count INTEGER NOT NULL DEFAULT 0, upstream_fetch_bytes INTEGER NOT NULL DEFAULT 0, transform_count INTEGER NOT NULL DEFAULT 0, last_updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); INSERT OR IGNORE INTO cache_stats (id) VALUES (1); -- A random value that a login session carries from its login. pixa makes it -- when the table has no row, and each logout replaces it; a login session -- carrying another value is refused, so logging out ends every login session. CREATE TABLE IF NOT EXISTS logout ( id INTEGER PRIMARY KEY CHECK (id = 1), logout_value TEXT NOT NULL );