Files
directus/db-init/001_extensions.sql
T
julian 25a9731070 Task 1.3 — Initial migrations
Three SQL files under db-init/ create the schema processor writes
against. All three apply cleanly via apply-db-init.sh, are idempotent
on re-run, and end with assertion blocks that catch silent
schema drift.

001_extensions.sql — registers timescaledb on the directus database.
  PostGIS deferred to Phase 2 (per Plan A). The timescaledb-ha image
  pre-creates the extension at DB init, so the IF NOT EXISTS guard
  fires as a NOTICE — expected and harmless.

002_positions_hypertable.sql — positions hypertable, exact
  column-by-column match against processor/src/db/migrations/0001_positions.sql.

  Cross-checking against processor surfaced 8 divergences from the
  original task spec; processor wins in every case (it is the writer
  and is in production). The corrections:

    - added ingested_at timestamptz NOT NULL DEFAULT now()
    - added codec text NOT NULL
    - altitude/angle/speed: real NOT NULL (not DOUBLE PRECISION nullable)
    - satellites/priority: NOT NULL
    - removed attributes DEFAULT '{}'::jsonb (processor always writes)
    - replaced PRIMARY KEY with UNIQUE INDEX positions_device_ts
      (idiomatic for TimescaleDB hypertables)
    - chunk interval 1 day, not 7 days
    - two indexes (positions_device_ts + positions_ts), not one composite

  Without these corrections every processor INSERT would have failed
  with NOT NULL violations. Spec deliverables section updated to
  reflect the correct shape so future readers see the right schema.

003_faulty_column.sql — adds the operator-controlled faulty boolean
  flag plus the partial index positions_faulty_idx ON (device_id,
  ts DESC) WHERE faulty = FALSE. The column is set only via Directus
  admin (Phase 4 permissions); processor's writer never touches it.
  The partial index optimises the hot-path read pattern (every
  processor evaluator filters faulty = FALSE); operator queries that
  look at faulty rows specifically use the broader positions_device_ts
  index from 002.

Live-verified 2026-05-01:
  - First apply: 3 applied, 0 skipped, exit 0.
  - Re-run: 0 applied, 3 skipped, exit 0.
  - All 13 columns present with correct types/nullability/defaults.
  - Hypertable registered with 1-day chunk interval.
  - Three expected indexes present.

Non-blocking observation: TimescaleDB's create_hypertable()
auto-created a fourth index (positions_ts_idx) duplicating our
explicit positions_ts. Processor's migration has the same redundancy
so stage already lives with this. Cleanup path documented in the
task spec for Phase 3 hardening (create_default_indexes => FALSE
in the create_hypertable call).

ROADMAP marks 1.3 done; 1.4 next.
2026-05-01 22:52:06 +02:00

33 lines
1.5 KiB
SQL

-- 001_extensions.sql
-- Registers the TimescaleDB extension on the directus database.
--
-- What this file does:
-- Enables TimescaleDB so that migration 002 can call create_hypertable().
-- CASCADE is included so any required dependency extensions install
-- transparently; for TimescaleDB on timescaledb-ha there are none, but
-- the clause is harmless and future-proof.
--
-- PostGIS is intentionally NOT registered here.
-- Decision: PostGIS lands in a separate migration when Phase 2
-- (geofences, SLZs, waypoints) is implemented. The timescaledb-ha image
-- ships the PostGIS binaries, so the binary is present; it just is not
-- registered on this database yet. The boot-time "PostGIS isn't installed"
-- warning from the processor service is benign and expected during Phase 1.
--
-- Idempotency: IF NOT EXISTS makes this a no-op if timescaledb is already
-- registered. Running this file twice produces no error.
CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;
-- Assertion: verify the extension is actually present after the statement
-- above. If the CREATE EXTENSION silently failed for any reason (e.g. binary
-- missing from the image), this block halts boot with an actionable message
-- rather than letting migration 002 fail with a less-obvious error.
DO $$ BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_extension WHERE extname = 'timescaledb'
) THEN
RAISE EXCEPTION 'timescaledb extension was not created — check that the timescaledb-ha image is being used and the binary is present';
END IF;
END $$;