-- +goose Up -- Initial Runtime Manager PostgreSQL schema. -- -- Three tables cover the durable surface of the service: -- * runtime_records — one row per game with the latest known runtime -- status and Docker container binding; -- * operation_log — append-only audit of every start/stop/restart/ -- patch/cleanup/reconcile_* operation RTM performed; -- * health_snapshots — latest technical health observation per game. -- -- Schema and the matching `rtmanagerservice` role are provisioned -- outside this script (in tests via cmd/jetgen/main.go::provisionRoleAndSchema; -- in production via an ops init script). This migration runs as the -- schema owner with `search_path=rtmanager` and only contains DDL for the -- service-owned tables and indexes. ARCHITECTURE.md §Database topology -- mandates that the per-service role's grants stay restricted to its own -- schema; consequently this file deliberately deviates from PLAN.md -- Stage 09's literal `CREATE SCHEMA IF NOT EXISTS rtmanager;` instruction. -- runtime_records holds one durable record per game with the latest -- known runtime status and Docker container binding. The status enum -- (running | stopped | removed) is enforced by a CHECK so domain code -- can rely on it without reading every callsite. The (status, last_op_at) -- index drives the periodic container-cleanup worker that scans -- `status='stopped' AND last_op_at < now() - retention`. CREATE TABLE runtime_records ( game_id text PRIMARY KEY, status text NOT NULL, current_container_id text, current_image_ref text, engine_endpoint text NOT NULL, state_path text NOT NULL, docker_network text NOT NULL, started_at timestamptz, stopped_at timestamptz, removed_at timestamptz, last_op_at timestamptz NOT NULL, created_at timestamptz NOT NULL, CONSTRAINT runtime_records_status_chk CHECK (status IN ('running', 'stopped', 'removed')) ); CREATE INDEX runtime_records_status_last_op_idx ON runtime_records (status, last_op_at); -- operation_log is an append-only audit of every operation Runtime -- Manager performed against a game's runtime. The (game_id, started_at -- DESC) index drives audit reads from the GM/Admin REST surface; -- finished_at is nullable for in-flight rows even though Stage 13+ -- always finalises the row in the same transaction. The op_kind / -- op_source / outcome enums are enforced by CHECK constraints to keep -- the audit schema honest without a separate Go validator. CREATE TABLE operation_log ( id bigserial PRIMARY KEY, game_id text NOT NULL, op_kind text NOT NULL, op_source text NOT NULL, source_ref text NOT NULL DEFAULT '', image_ref text NOT NULL DEFAULT '', container_id text NOT NULL DEFAULT '', outcome text NOT NULL, error_code text NOT NULL DEFAULT '', error_message text NOT NULL DEFAULT '', started_at timestamptz NOT NULL, finished_at timestamptz, CONSTRAINT operation_log_op_kind_chk CHECK (op_kind IN ( 'start', 'stop', 'restart', 'patch', 'cleanup_container', 'reconcile_adopt', 'reconcile_dispose' )), CONSTRAINT operation_log_op_source_chk CHECK (op_source IN ( 'lobby_stream', 'gm_rest', 'admin_rest', 'auto_ttl', 'auto_reconcile' )), CONSTRAINT operation_log_outcome_chk CHECK (outcome IN ('success', 'failure')) ); CREATE INDEX operation_log_game_started_idx ON operation_log (game_id, started_at DESC); -- health_snapshots stores the latest technical health observation per -- game. One row per game; later observations overwrite. The status enum -- mirrors the `event_type` vocabulary on `runtime:health_events` -- (collapsed to a flat status column for the latest-observation view). CREATE TABLE health_snapshots ( game_id text PRIMARY KEY, container_id text NOT NULL DEFAULT '', status text NOT NULL, source text NOT NULL, details jsonb NOT NULL DEFAULT '{}'::jsonb, observed_at timestamptz NOT NULL, CONSTRAINT health_snapshots_status_chk CHECK (status IN ( 'healthy', 'probe_failed', 'exited', 'oom', 'inspect_unhealthy', 'container_disappeared' )), CONSTRAINT health_snapshots_source_chk CHECK (source IN ('docker_event', 'inspect', 'probe')) ); -- +goose Down DROP TABLE IF EXISTS health_snapshots; DROP TABLE IF EXISTS operation_log; DROP TABLE IF EXISTS runtime_records;