fix(jobengine): make all orchestrator migration SQL idempotent and PostgreSQL-compatible
Fix 4 classes of issues that prevented JobEngine from auto-migrating: 1. Non-idempotent DDL: add IF NOT EXISTS to CREATE TABLE, wrap CREATE TYPE in DO blocks with EXCEPTION WHEN duplicate_object, wrap partition creation with EXCEPTION WHEN duplicate_object OR SQLSTATE '42P17' 2. Reserved keyword: quote `window` column name in 004_slo_quotas.sql 3. Invalid syntax: replace DELETE...LIMIT with ctid subquery pattern in 004_slo_quotas.sql and 005_audit_ledger.sql 4. Partition constraint: add tenant_id to UNIQUE(log_id) constraint on pack_run_logs in 006_pack_runs.sql (partitioned tables require partition key in all unique constraints) 5. Non-immutable index predicate: remove NOW() from partial index predicate in 002_backfill.sql 6. Remove BEGIN/COMMIT wrappers from all migration files (the StartupMigrationHost already wraps each migration in a transaction) All 8 orchestrator migrations (001-008) now apply cleanly on fresh DB. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,22 +1,23 @@
|
||||
-- 002_backfill.sql
|
||||
-- Backfill and watermark tables for event-time window tracking (ORCH-SVC-33-003)
|
||||
-- Adds watermarks, backfill_requests, and processed_events for duplicate suppression.
|
||||
|
||||
BEGIN;
|
||||
-- All statements are idempotent so the migration is safe on pre-existing databases.
|
||||
|
||||
-- Backfill request status
|
||||
CREATE TYPE backfill_status AS ENUM (
|
||||
'pending',
|
||||
'validating',
|
||||
'running',
|
||||
'paused',
|
||||
'completed',
|
||||
'failed',
|
||||
'canceled'
|
||||
);
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE backfill_status AS ENUM (
|
||||
'pending',
|
||||
'validating',
|
||||
'running',
|
||||
'paused',
|
||||
'completed',
|
||||
'failed',
|
||||
'canceled'
|
||||
);
|
||||
EXCEPTION WHEN duplicate_object OR SQLSTATE '42P17' THEN NULL; END $$;
|
||||
|
||||
-- Watermarks: Per-source/job-type event-time cursors
|
||||
CREATE TABLE watermarks (
|
||||
CREATE TABLE IF NOT EXISTS watermarks (
|
||||
watermark_id UUID NOT NULL,
|
||||
tenant_id TEXT NOT NULL,
|
||||
source_id UUID,
|
||||
@@ -35,13 +36,15 @@ CREATE TABLE watermarks (
|
||||
CONSTRAINT ck_watermarks_hash_hex CHECK (last_batch_hash IS NULL OR last_batch_hash ~ '^[0-9a-f]{64}$')
|
||||
) PARTITION BY LIST (tenant_id);
|
||||
|
||||
CREATE TABLE watermarks_default PARTITION OF watermarks DEFAULT;
|
||||
DO $$ BEGIN
|
||||
CREATE TABLE watermarks_default PARTITION OF watermarks DEFAULT;
|
||||
EXCEPTION WHEN duplicate_object OR SQLSTATE '42P17' THEN NULL; END $$;
|
||||
|
||||
CREATE INDEX ix_watermarks_source ON watermarks (tenant_id, source_id) WHERE source_id IS NOT NULL;
|
||||
CREATE INDEX ix_watermarks_job_type ON watermarks (tenant_id, job_type) WHERE job_type IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS ix_watermarks_source ON watermarks (tenant_id, source_id) WHERE source_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS ix_watermarks_job_type ON watermarks (tenant_id, job_type) WHERE job_type IS NOT NULL;
|
||||
|
||||
-- Backfill Requests: Batch reprocessing operations
|
||||
CREATE TABLE backfill_requests (
|
||||
CREATE TABLE IF NOT EXISTS backfill_requests (
|
||||
backfill_id UUID NOT NULL,
|
||||
tenant_id TEXT NOT NULL,
|
||||
source_id UUID,
|
||||
@@ -79,14 +82,16 @@ CREATE TABLE backfill_requests (
|
||||
CONSTRAINT ck_backfill_batch_size CHECK (batch_size > 0 AND batch_size <= 10000)
|
||||
) PARTITION BY LIST (tenant_id);
|
||||
|
||||
CREATE TABLE backfill_requests_default PARTITION OF backfill_requests DEFAULT;
|
||||
DO $$ BEGIN
|
||||
CREATE TABLE backfill_requests_default PARTITION OF backfill_requests DEFAULT;
|
||||
EXCEPTION WHEN duplicate_object OR SQLSTATE '42P17' THEN NULL; END $$;
|
||||
|
||||
CREATE INDEX ix_backfill_status ON backfill_requests (tenant_id, status, created_at DESC);
|
||||
CREATE INDEX ix_backfill_scope ON backfill_requests (tenant_id, scope_key, created_at DESC);
|
||||
CREATE INDEX ix_backfill_running ON backfill_requests (tenant_id, source_id, job_type) WHERE status IN ('running', 'validating');
|
||||
CREATE INDEX IF NOT EXISTS ix_backfill_status ON backfill_requests (tenant_id, status, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS ix_backfill_scope ON backfill_requests (tenant_id, scope_key, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS ix_backfill_running ON backfill_requests (tenant_id, source_id, job_type) WHERE status IN ('running', 'validating');
|
||||
|
||||
-- Processed Events: Duplicate suppression tracking (TTL-managed)
|
||||
CREATE TABLE processed_events (
|
||||
CREATE TABLE IF NOT EXISTS processed_events (
|
||||
tenant_id TEXT NOT NULL,
|
||||
scope_key TEXT NOT NULL,
|
||||
event_key TEXT NOT NULL, -- Unique identifier for deduplication
|
||||
@@ -97,14 +102,16 @@ CREATE TABLE processed_events (
|
||||
CONSTRAINT pk_processed_events PRIMARY KEY (tenant_id, scope_key, event_key)
|
||||
) PARTITION BY LIST (tenant_id);
|
||||
|
||||
CREATE TABLE processed_events_default PARTITION OF processed_events DEFAULT;
|
||||
DO $$ BEGIN
|
||||
CREATE TABLE processed_events_default PARTITION OF processed_events DEFAULT;
|
||||
EXCEPTION WHEN duplicate_object OR SQLSTATE '42P17' THEN NULL; END $$;
|
||||
|
||||
CREATE INDEX ix_processed_events_expires ON processed_events (expires_at) WHERE expires_at < NOW() + INTERVAL '1 day';
|
||||
CREATE INDEX ix_processed_events_time ON processed_events (tenant_id, scope_key, event_time DESC);
|
||||
CREATE INDEX ix_processed_events_batch ON processed_events (tenant_id, batch_id) WHERE batch_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS ix_processed_events_expires ON processed_events (expires_at);
|
||||
CREATE INDEX IF NOT EXISTS ix_processed_events_time ON processed_events (tenant_id, scope_key, event_time DESC);
|
||||
CREATE INDEX IF NOT EXISTS ix_processed_events_batch ON processed_events (tenant_id, batch_id) WHERE batch_id IS NOT NULL;
|
||||
|
||||
-- Backfill Checkpoints: Resumable batch processing state
|
||||
CREATE TABLE backfill_checkpoints (
|
||||
CREATE TABLE IF NOT EXISTS backfill_checkpoints (
|
||||
checkpoint_id UUID NOT NULL,
|
||||
tenant_id TEXT NOT NULL,
|
||||
backfill_id UUID NOT NULL,
|
||||
@@ -126,9 +133,11 @@ CREATE TABLE backfill_checkpoints (
|
||||
CONSTRAINT ck_backfill_checkpoints_hash_hex CHECK (batch_hash IS NULL OR batch_hash ~ '^[0-9a-f]{64}$')
|
||||
) PARTITION BY LIST (tenant_id);
|
||||
|
||||
CREATE TABLE backfill_checkpoints_default PARTITION OF backfill_checkpoints DEFAULT;
|
||||
DO $$ BEGIN
|
||||
CREATE TABLE backfill_checkpoints_default PARTITION OF backfill_checkpoints DEFAULT;
|
||||
EXCEPTION WHEN duplicate_object OR SQLSTATE '42P17' THEN NULL; END $$;
|
||||
|
||||
CREATE INDEX ix_backfill_checkpoints_request ON backfill_checkpoints (tenant_id, backfill_id, batch_number);
|
||||
CREATE INDEX IF NOT EXISTS ix_backfill_checkpoints_request ON backfill_checkpoints (tenant_id, backfill_id, batch_number);
|
||||
|
||||
-- Function to clean up expired processed events (called by background job)
|
||||
CREATE OR REPLACE FUNCTION cleanup_expired_processed_events(batch_limit INTEGER DEFAULT 10000)
|
||||
@@ -150,5 +159,3 @@ BEGIN
|
||||
RETURN deleted_count;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
COMMIT;
|
||||
|
||||
Reference in New Issue
Block a user