From 065bd82a45f4095d8b2c381d4a2d97a2667a2b3c Mon Sep 17 00:00:00 2001 From: gadgethd <111318106+gadgethd@users.noreply.github.com> Date: Sat, 19 Sep 2026 12:26:25 +0000 Subject: [PATCH] fix(db): add message_tags schema migration (tables existed only via the optional worker) The backend now reads message_tags for feed enrichment, packet detail and the WebSocket initial state, but the table was created only by tagger_worker.py startup DDL -- which exits 2 without TYPESAFE_API_KEY, so fresh stacks had no table, WS connects were closed 1013 and the CI smoke fanout check timed out. Idempotent create; no-op where the worker already made the tables. --- .../src/db/migrations/055_message_tags.sql | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 backend/src/db/migrations/055_message_tags.sql diff --git a/backend/src/db/migrations/055_message_tags.sql b/backend/src/db/migrations/055_message_tags.sql new file mode 100644 index 0000000..49dc7e2 --- /dev/null +++ b/backend/src/db/migrations/055_message_tags.sql @@ -0,0 +1,51 @@ +-- 055: message_tags + tagger bookkeeping schema. +-- +-- The core backend reads message_tags for feed enrichment, packet detail and +-- the WebSocket initial state, but the table was previously created only by +-- tagger_worker.py's startup DDL -- which runs after a TYPESAFE_API_KEY gate +-- and exits before any DDL when the key is unset. Fresh stacks therefore had +-- no table: WS clients were closed with 1013 on connect and the CI smoke's +-- MQTT->WebSocket fanout check timed out. +-- +-- The schema now lives in the migration ledger so every deployment has it +-- independently of the optional worker; the worker's CREATE TABLE IF NOT +-- EXISTS stays as a harmless no-op. Prefix 055 deliberately leaves 052-054 +-- reserved for the owner packet sharing workstream. + +SET LOCAL lock_timeout = '30s'; +SET LOCAL statement_timeout = '0'; + +CREATE TABLE IF NOT EXISTS message_tags ( + packet_hash text PRIMARY KEY, + packet_time timestamptz NOT NULL, + tagged_at timestamptz NOT NULL DEFAULT now(), + model text, + latency_ms integer, + tags jsonb NOT NULL, + confidence jsonb +); + +CREATE TABLE IF NOT EXISTS tagger_state ( + id int PRIMARY KEY CHECK (id = 1), + cursor_time timestamptz, + seen bigint NOT NULL DEFAULT 0, + tagged bigint NOT NULL DEFAULT 0, + errors bigint NOT NULL DEFAULT 0, + http_429 bigint NOT NULL DEFAULT 0, + last_error text, + avg_latency_ms_50 real, + started_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz +); +INSERT INTO tagger_state (id) VALUES (1) ON CONFLICT DO NOTHING; +ALTER TABLE tagger_state ADD COLUMN IF NOT EXISTS retried bigint NOT NULL DEFAULT 0; +ALTER TABLE tagger_state ADD COLUMN IF NOT EXISTS given_up bigint NOT NULL DEFAULT 0; + +CREATE TABLE IF NOT EXISTS tagger_retry ( + packet_hash text PRIMARY KEY, + packet_time timestamptz NOT NULL, + attempts int NOT NULL DEFAULT 0, + last_error text, + first_failed_at timestamptz NOT NULL DEFAULT now(), + exhausted boolean NOT NULL DEFAULT false +);