"""videos.published_at NOT NULL DEFAULT now() (PERF-15) Every video in practice carries a publish date (0 NULLs in the 257k-row prod-sized reseed), but the column was nullable, which forced the feed's keyset predicate into a non-sargable `... OR published_at IS NULL` disjunction — blocking the ix_videos_published_at_desc range so deep `newest` pages scanned from the top of the index and filtered (51.8k buffers). Making it NOT NULL lets the keyset drop that disjunction and take a `published_at <=` range bound (deep page -> ~0.5k buffers). server_default now() is the safety net for the rare date-less ingest path (the enrich overwrites it with the real date); the app-side ingest paths also fall back to now() so they never write an explicit NULL. Note: `SET NOT NULL` validates with a brief ACCESS EXCLUSIVE lock + one table scan (it cannot run CONCURRENTLY). On the prod row count (~235k) that is sub-second; still, it is a short write-blocking lock rather than the ~0 of 0067's CONCURRENTLY index build. """ from typing import Sequence, Union import sqlalchemy as sa from alembic import op revision: str = "0068_published_at_not_null" down_revision: Union[str, None] = "0067_feed_sort_indexes" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: # Backfill any stray NULLs first so SET NOT NULL can't fail (0 in prod, but be safe). op.execute("UPDATE videos SET published_at = now() WHERE published_at IS NULL") op.alter_column("videos", "published_at", nullable=False, server_default=sa.text("now()")) def downgrade() -> None: op.alter_column("videos", "published_at", nullable=True, server_default=None)