[AZ-304] C6 Postgres schema: additive 0002 migration + UUIDv5

Strictly additive Alembic migration on the AZ-263 baseline (data_model
.md § 6.1 / § 6.3): six new tiles columns (tile_uuid UNIQUE,
location_hash, content_sha256, disk_bytes, accessed_at, uploaded_at),
four new btree indices, one UNIQUE expression index over the
COALESCE-zero-uuid natural key, CHECK widening of
ck_tiles_freshness_status to the AZ-263 + AZ-303 vocabulary UNION,
four NULLable bbox columns on sector_classifications, and a new
tile_freshness_rules table seeded with the two default thresholds.

Pinned UUIDv5 namespace (TILE_NAMESPACE_UUID =
5b8d0c2e-1a4f-4b3a-8c9d-e7f6a3b2c1d0) + derive_tile_id /
derive_location_hash helpers cross-coordinated with
satellite-provider. Migration runner apply_migrations(config) drives
Alembic command.upgrade("head") against the AZ-263 env with one
retry on PG SQLSTATE 40001 and structured INFO logs on apply / no-op.

Contract bump tile_metadata_store.md v1.1.0 -> v1.2.0 adds
TileMetadata.location_hash: UUID | None = None (non-breaking).
module-layout.md updated so c6_tile_cache explicitly Owns
db/migrations/**.

Tier-1 tests: UUIDv5 determinism + locked vectors + DSN resolution +
retry mocked DBAPIError -> 1180 passed, 32 skipped. Tier-2 docker
schema tests gated by @pytest.mark.docker run against the existing
docker-compose.test.yml db service.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-12 17:05:41 +03:00
parent 21f5a30d09
commit dde838d2cc
15 changed files with 2084 additions and 25 deletions
@@ -0,0 +1,156 @@
-- AZ-304 — Expected post-0002 c6_tile_cache schema (Postgres 16).
--
-- Human-readable canonical DDL for the schema state produced by applying
-- 0001_initial (AZ-263) + 0002_c6_tile_identity_and_lru (AZ-304). The
-- companion test `test_postgres_schema.py` introspects information_schema /
-- pg_indexes / pg_constraint / tile_freshness_rules row contents and
-- asserts each artifact below exists with the documented shape.
--
-- Updating this file without a corresponding migration revision is a
-- Spec-Gap finding (High) at code review.
-- =====================================================================
-- 0001_initial (AZ-263) — unchanged baseline
-- =====================================================================
CREATE TABLE flights (
id UUID PRIMARY KEY,
companion_id TEXT NOT NULL,
started_at TIMESTAMPTZ NOT NULL DEFAULT now(),
landed_at TIMESTAMPTZ,
metadata JSONB
);
CREATE TABLE sector_classifications (
id BIGSERIAL PRIMARY KEY,
sector_id TEXT NOT NULL UNIQUE,
classification TEXT NOT NULL,
freshness_threshold_days INTEGER NOT NULL,
-- 0002 additive bbox columns (operator-populated pre-flight; NULL pre-population):
min_lat DOUBLE PRECISION,
min_lon DOUBLE PRECISION,
max_lat DOUBLE PRECISION,
max_lon DOUBLE PRECISION
);
CREATE TABLE tiles (
id BIGSERIAL PRIMARY KEY,
-- Canonical columns mirrored from satellite-provider (AZ-263):
zoom_level INTEGER NOT NULL,
tile_x INTEGER NOT NULL,
tile_y INTEGER NOT NULL,
latitude DOUBLE PRECISION NOT NULL,
longitude DOUBLE PRECISION NOT NULL,
tile_size_meters DOUBLE PRECISION NOT NULL,
tile_size_pixels INTEGER NOT NULL,
capture_timestamp TIMESTAMPTZ NOT NULL,
compression TEXT NOT NULL DEFAULT 'jpeg',
crs TEXT NOT NULL DEFAULT 'EPSG:3857',
source TEXT NOT NULL,
-- Additive onboard-only columns (AZ-263):
flight_id UUID REFERENCES flights(id),
companion_id TEXT,
tile_quality_metadata JSONB,
voting_status TEXT,
freshness_status TEXT NOT NULL DEFAULT 'fresh',
signature BYTEA,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
-- 0002 additive identity / LRU / disk-budget columns:
tile_uuid UUID NOT NULL UNIQUE,
location_hash UUID NOT NULL,
content_sha256 TEXT NOT NULL,
disk_bytes BIGINT NOT NULL,
accessed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
uploaded_at TIMESTAMPTZ,
-- AZ-263 CHECKs (preserved):
CONSTRAINT ck_tiles_zoom CHECK (zoom_level BETWEEN 10 AND 22),
CONSTRAINT ck_tiles_meters CHECK (tile_size_meters > 0),
CONSTRAINT ck_tiles_pixels CHECK (tile_size_pixels > 0),
CONSTRAINT ck_tiles_source CHECK (source IN ('googlemaps','onboard_ingest')),
CONSTRAINT ck_tiles_voting_status CHECK (
voting_status IS NULL OR voting_status IN ('pending','trusted','rejected')
),
-- 0002 widened CHECK (UNION of AZ-263 + AZ-303 vocabularies):
CONSTRAINT ck_tiles_freshness_status CHECK (
freshness_status IN (
'fresh','stale_warn','stale_reject',
'stale_active_conflict','stale_rear','downgraded'
)
),
-- 0002 additive CHECKs:
CONSTRAINT ck_tiles_content_sha256_len CHECK (length(content_sha256) = 64),
CONSTRAINT ck_tiles_disk_bytes_nonneg CHECK (disk_bytes >= 0)
);
-- AZ-263 indices (preserved):
CREATE INDEX ix_tiles_zxy ON tiles (zoom_level, tile_x, tile_y);
CREATE INDEX ix_tiles_lat_lon ON tiles (latitude, longitude);
CREATE INDEX ix_tiles_voting_status_onboard
ON tiles (voting_status)
WHERE source = 'onboard_ingest';
CREATE INDEX ix_tiles_flight_id ON tiles (flight_id);
CREATE INDEX ix_tiles_created_at ON tiles (created_at);
-- 0002 additive indices:
CREATE UNIQUE INDEX idx_tiles_natural_key ON tiles (
zoom_level,
tile_x,
tile_y,
tile_size_meters,
source,
COALESCE(flight_id, '00000000-0000-0000-0000-000000000000'::uuid)
);
CREATE INDEX idx_tiles_location_hash ON tiles (location_hash);
CREATE INDEX idx_tiles_accessed_at ON tiles (accessed_at);
CREATE INDEX idx_tiles_pending_upload ON tiles (uploaded_at)
WHERE source = 'onboard_ingest' AND uploaded_at IS NULL;
CREATE INDEX idx_tiles_flight_captured ON tiles (flight_id, capture_timestamp)
WHERE flight_id IS NOT NULL;
-- Note: an automatic UNIQUE btree on `tiles.tile_uuid` is created by the
-- column-level UNIQUE; PG names it `tiles_tile_uuid_key` (system-generated).
-- The diff test does NOT assert that name; it asserts presence of a UNIQUE
-- index covering exactly `(tile_uuid)`.
CREATE TABLE manifests (
id BIGSERIAL PRIMARY KEY,
manifest_id TEXT NOT NULL UNIQUE,
flight_id UUID NOT NULL REFERENCES flights(id),
content_hash TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
payload JSONB NOT NULL
);
CREATE TABLE engine_cache_entries (
id BIGSERIAL PRIMARY KEY,
engine_path TEXT NOT NULL,
sm_arch TEXT NOT NULL,
jetpack_version TEXT NOT NULL,
tensorrt_version TEXT NOT NULL,
precision TEXT NOT NULL,
content_hash TEXT NOT NULL UNIQUE,
int8_calibration_path TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- =====================================================================
-- 0002_c6_tile_identity_and_lru (AZ-304) — new table
-- =====================================================================
CREATE TABLE tile_freshness_rules (
classification TEXT PRIMARY KEY,
max_age_seconds BIGINT NOT NULL,
action TEXT NOT NULL,
set_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT ck_tfr_action CHECK (action IN ('reject','downgrade')),
CONSTRAINT ck_tfr_max_age_pos CHECK (max_age_seconds > 0)
);
-- Seed rows applied by the 0002 migration (AC-7):
INSERT INTO tile_freshness_rules (classification, max_age_seconds, action) VALUES
('active_conflict', 15552000, 'reject'), -- 6 months × 30 days × 86400 s
('stable_rear', 31104000, 'downgrade'); -- 12 months × 30 days × 86400 s