Mechanical, whole-tree `ruff format` pass — no behaviour change. Recorded in .git-blame-ignore-revs (next commit) so git blame skips it.
36 lines
1.7 KiB
Python
36 lines
1.7 KiB
Python
"""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)
|