feat: migration to add superseded column with backfill and partial index

This commit is contained in:
2026-03-18 23:31:17 -04:00
parent f84b20269a
commit 7268d06b4f
@@ -0,0 +1,55 @@
"""Add superseded column to downloads
Revision ID: 003
Revises: 002
Create Date: 2026-03-18
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = '003'
down_revision: Union[str, None] = '002'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# Add superseded column with default False
op.add_column(
'downloads',
sa.Column('superseded', sa.Boolean(), nullable=False, server_default='false')
)
# Backfill: mark FAILED downloads as superseded where a later COMPLETED
# download exists for the same source. REQUIRED — without this, all
# previously-superseded failures reappear in the dashboard immediately.
op.execute("""
UPDATE downloads d
SET superseded = TRUE
WHERE d.status = 'failed'
AND EXISTS (
SELECT 1 FROM downloads d2
WHERE d2.source_id = d.source_id
AND d2.status = 'completed'
AND d2.created_at > d.created_at
)
""")
# Partial index covering the dominant query pattern: status + superseded = FALSE.
# A single boolean column index would be ignored in favour of status indexes;
# this form matches the WHERE clauses at all three affected query sites.
op.create_index(
'idx_downloads_not_superseded',
'downloads',
['status'],
postgresql_where=sa.text('superseded = FALSE')
)
def downgrade() -> None:
op.drop_index('idx_downloads_not_superseded', table_name='downloads')
op.drop_column('downloads', 'superseded')