-- 005_races.sql — race plan, finished race archive, queue. -- -- Storage split: -- * Active races (lobby/countdown/racing) live in-memory in lobby.Service. -- * Finished races are snapshotted into finished_races on SetRaceStatus(finished). -- * Race plans describe recurring / one-off scheduled races; the scheduler -- materializes them into lobby.RaceMeta as their start_at_ms approaches. -- * Queue entries are per-driver per-race_id subscriptions to the next -- upcoming races. Driver is auto-attached when a slot opens. -- -- Keyset pagination: -- * finished_races ordered by (finished_ms DESC, id DESC). Cursor encodes -- (finished_ms, id); lookup uses (finished_ms, id) < (cursor_ms, cursor_id). -- * race_plans ordered by (start_at_ms ASC, id ASC). Cursor is -- (start_at_ms, id) with strict-less comparison. CREATE TABLE IF NOT EXISTS finished_races ( id TEXT PRIMARY KEY, name TEXT NOT NULL, host_id TEXT NOT NULL, host_name TEXT NOT NULL DEFAULT '', track_id TEXT NOT NULL DEFAULT '', max_cars INT NOT NULL, laps INT NOT NULL, time_limit_s INT NOT NULL, driver_ids TEXT[] NOT NULL DEFAULT '{}', status TEXT NOT NULL DEFAULT 'finished' CHECK (status IN ('finished', 'cancelled')), created_ms BIGINT NOT NULL, started_ms BIGINT NOT NULL DEFAULT 0, finished_ms BIGINT NOT NULL, duration_ms BIGINT NOT NULL DEFAULT 0, total_laps INT NOT NULL DEFAULT 0, total_drivers INT NOT NULL DEFAULT 0, winner_driver_id TEXT NOT NULL DEFAULT '', winner_name TEXT NOT NULL DEFAULT '', best_lap_ms BIGINT NOT NULL DEFAULT 0 ); CREATE INDEX IF NOT EXISTS finished_races_finished_ms_id_idx ON finished_races (finished_ms DESC, id DESC); CREATE INDEX IF NOT EXISTS finished_races_host_idx ON finished_races (host_id); CREATE INDEX IF NOT EXISTS finished_races_track_idx ON finished_races (track_id); CREATE INDEX IF NOT EXISTS finished_races_status_idx ON finished_races (status); -- --------------------------------------------------------------------------- -- Race plans -- --------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS race_plans ( id TEXT PRIMARY KEY, name TEXT NOT NULL, track_id TEXT NOT NULL DEFAULT 'default', max_cars INT NOT NULL, laps INT NOT NULL DEFAULT 0, time_limit_s INT NOT NULL DEFAULT 0, host_id TEXT NOT NULL, host_name TEXT NOT NULL DEFAULT '', start_at_ms BIGINT NOT NULL, interval_s INT NOT NULL DEFAULT 0, -- 0 = single-shot count INT NOT NULL DEFAULT 0, -- 0 = repeat forever enabled BOOLEAN NOT NULL DEFAULT TRUE, created_ms BIGINT NOT NULL, updated_ms BIGINT NOT NULL, next_fire_ms BIGINT NOT NULL, -- last materialised fire time fires_done INT NOT NULL DEFAULT 0 ); CREATE INDEX IF NOT EXISTS race_plans_start_idx ON race_plans (start_at_ms ASC, id ASC); CREATE INDEX IF NOT EXISTS race_plans_enabled_idx ON race_plans (enabled, next_fire_ms); -- --------------------------------------------------------------------------- -- Queue -- --------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS race_queue ( driver_id TEXT NOT NULL, race_id TEXT NOT NULL, plan_id TEXT NOT NULL DEFAULT '', enqueued_ms BIGINT NOT NULL, PRIMARY KEY (driver_id, race_id) ); CREATE INDEX IF NOT EXISTS race_queue_race_idx ON race_queue (race_id); CREATE INDEX IF NOT EXISTS race_queue_driver_idx ON race_queue (driver_id, enqueued_ms);