Switch to timescaledb-ha image; enable PostGIS in migration

Migration 0001_positions.sql now runs CREATE EXTENSION IF NOT EXISTS
postgis alongside timescaledb. PostGIS isn't used in Phase 1 but enabling
it now means Phase 2's geofence engine doesn't need a separate migration
step. The deploy stack uses the timescale/timescaledb-ha:*-all image
which ships both extensions.

Integration test (pipeline.integration.test.ts) updated to use the same
timescale/timescaledb-ha:pg16.6-ts2.17.2-all image as the deploy stack.
Stock POSTGRES_USER/PASSWORD/DB env vars retained — if recent ha-image
revisions don't accept them, the test container will fail clearly on
first run with Docker, and we'll switch to the right env-var scheme.
This commit is contained in:
2026-05-01 10:37:31 +02:00
parent 10e20c4038
commit 88cc98f3cc
2 changed files with 18 additions and 7 deletions
+10 -1
View File
@@ -6,8 +6,17 @@
-- writes to (timing_records, stage_results, etc.) are defined in Directus. -- writes to (timing_records, stage_results, etc.) are defined in Directus.
-- Do NOT modify this table from the Directus admin UI. -- Do NOT modify this table from the Directus admin UI.
-- Enable TimescaleDB extension (no-op if already installed at the DB level). -- Enable required Postgres extensions. No-ops if already installed at the
-- DB level. The deploy stack uses the `timescale/timescaledb-ha:*-all`
-- image which ships TimescaleDB and PostGIS binaries — these CREATE
-- EXTENSION statements just register them on this database.
--
-- TimescaleDB: hypertable partitioning for the positions table below.
-- PostGIS: not used in Phase 1, but enabled now so Phase 2's geofence
-- engine doesn't need a separate migration step. Geometry columns will
-- be added by the Directus-owned migrations for the geofences table.
CREATE EXTENSION IF NOT EXISTS timescaledb; CREATE EXTENSION IF NOT EXISTS timescaledb;
CREATE EXTENSION IF NOT EXISTS postgis;
-- Raw position history. High-volume append-only table; the hypertable -- Raw position history. High-volume append-only table; the hypertable
-- partitioning column is `ts` (device-reported GPS time). -- partitioning column is `ts` (device-reported GPS time).
+8 -6
View File
@@ -1,9 +1,11 @@
/** /**
* Integration test: end-to-end pipeline round-trip via testcontainers. * Integration test: end-to-end pipeline round-trip via testcontainers.
* *
* Spins up Redis 7 and TimescaleDB (timescale/timescaledb:latest-pg16) containers, * Spins up Redis 7 and TimescaleDB-HA (timescale/timescaledb-ha:pg16.6-ts2.17.2-all,
* runs the Processor migration, starts the consumer pipeline, publishes synthetic * matching the deploy stack image) containers, runs the Processor migration,
* Position records, and asserts the resulting rows in `positions`. * starts the consumer pipeline, publishes synthetic Position records, and asserts
* the resulting rows in `positions`. The `-all` image variant ships PostGIS too,
* so the migration's `CREATE EXTENSION IF NOT EXISTS postgis` succeeds.
* *
* If Docker is unavailable (CI runner without Docker, local dev without Docker * If Docker is unavailable (CI runner without Docker, local dev without Docker
* Desktop), the suite skips — it does not fail the build. Docker availability is * Desktop), the suite skips — it does not fail the build. Docker availability is
@@ -139,9 +141,9 @@ beforeAll(async () => {
return; return;
} }
// --- Step 2: start TimescaleDB container ------------------------------------ // --- Step 2: start TimescaleDB-HA container (same image as deploy stack) ---
try { try {
pgContainer = await new GenericContainer('timescale/timescaledb:latest-pg16') pgContainer = await new GenericContainer('timescale/timescaledb-ha:pg16.6-ts2.17.2-all')
.withExposedPorts(5432) .withExposedPorts(5432)
.withEnvironment({ .withEnvironment({
POSTGRES_USER: 'postgres', POSTGRES_USER: 'postgres',
@@ -152,7 +154,7 @@ beforeAll(async () => {
.start(); .start();
} catch (err) { } catch (err) {
console.warn( console.warn(
`[pipeline.integration.test] Failed to start TimescaleDB container: ${String(err)} — skipping`, `[pipeline.integration.test] Failed to start TimescaleDB-HA container: ${String(err)} — skipping`,
); );
dockerAvailable = false; dockerAvailable = false;
await redisContainer?.stop().catch(() => {}); await redisContainer?.stop().catch(() => {});