"""Create PostgreSQL-backed jobs, attempts and transition events.""" from __future__ import annotations import sqlalchemy as sa from alembic import op revision = "0008_jobs" down_revision = "0007_user_grants" branch_labels = None depends_on = None def upgrade() -> None: op.execute(sa.text("CREATE SCHEMA IF NOT EXISTS jobs")) op.create_table( "jobs", sa.Column("id", sa.Uuid(), primary_key=True), sa.Column("job_key", sa.String(512), nullable=False, unique=True), sa.Column("operation", sa.String(128), nullable=False), sa.Column("target_key", sa.String(256), nullable=False), sa.Column("profile_version", sa.String(128), nullable=False), sa.Column("payload", sa.JSON(), nullable=False), sa.Column("status", sa.String(32), nullable=False), sa.Column("available_at", sa.DateTime(timezone=True), nullable=False), sa.Column("max_attempts", sa.Integer(), nullable=False), sa.Column("attempt_count", sa.Integer(), nullable=False), sa.Column("leased_by", sa.String(128), nullable=True), sa.Column("lease_until", sa.DateTime(timezone=True), nullable=True), sa.Column("trace_id", sa.String(128), nullable=False), sa.Column("last_error_code", sa.String(128), nullable=True), sa.Column("last_error_details", sa.JSON(), nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), schema="jobs", ) op.create_index( "ix_jobs_ready", "jobs", ["status", "available_at", "lease_until"], schema="jobs" ) op.create_table( "attempts", sa.Column("id", sa.Uuid(), primary_key=True), sa.Column("job_id", sa.Uuid(), nullable=False), sa.Column("attempt_number", sa.Integer(), nullable=False), sa.Column("worker_id", sa.String(128), nullable=False), sa.Column("status", sa.String(32), nullable=False), sa.Column("leased_at", sa.DateTime(timezone=True), nullable=False), sa.Column("lease_until", sa.DateTime(timezone=True), nullable=False), sa.Column("heartbeat_at", sa.DateTime(timezone=True), nullable=False), sa.Column("finished_at", sa.DateTime(timezone=True), nullable=True), sa.Column("error_code", sa.String(128), nullable=True), sa.Column("error_details", sa.JSON(), nullable=False), sa.ForeignKeyConstraint(["job_id"], ["jobs.jobs.id"], ondelete="CASCADE"), sa.UniqueConstraint("job_id", "attempt_number", name="uq_jobs_attempt_number"), schema="jobs", ) op.create_index("ix_jobs_attempts_job", "attempts", ["job_id", "attempt_number"], schema="jobs") op.create_table( "events", sa.Column("id", sa.Uuid(), primary_key=True), sa.Column("job_id", sa.Uuid(), nullable=False), sa.Column("event_type", sa.String(64), nullable=False), sa.Column("occurred_at", sa.DateTime(timezone=True), nullable=False), sa.Column("trace_id", sa.String(128), nullable=False), sa.Column("duration_ms", sa.Integer(), nullable=True), sa.Column("error_code", sa.String(128), nullable=True), sa.Column("details", sa.JSON(), nullable=False), sa.ForeignKeyConstraint(["job_id"], ["jobs.jobs.id"], ondelete="CASCADE"), schema="jobs", ) op.create_index("ix_jobs_events_job_time", "events", ["job_id", "occurred_at"], schema="jobs") def downgrade() -> None: op.drop_index("ix_jobs_events_job_time", table_name="events", schema="jobs") op.drop_table("events", schema="jobs") op.drop_index("ix_jobs_attempts_job", table_name="attempts", schema="jobs") op.drop_table("attempts", schema="jobs") op.drop_index("ix_jobs_ready", table_name="jobs", schema="jobs") op.drop_table("jobs", schema="jobs") op.execute(sa.text("DROP SCHEMA IF EXISTS jobs"))