Files
docqube_backend/alembic/versions/b6_4_index_review.py
T
2026-09-08 11:00:05 +05:30

66 lines
2.8 KiB
Python

"""B6.4 — indexes on the foreign keys that queries actually filter by
The database has 34 foreign keys with no supporting index. PostgreSQL does not
create one automatically, and the cost is invisible on a seeded database: a
sequential scan over five rows is instant, over five hundred thousand it is not.
This adds indexes only where a measured query path filters or joins on the
column — the ones surfaced by the B6.3 query-budget work. Indexing all 34 would
add write cost for paths nothing traverses; the remainder are listed in
`tests/probes/test_index_coverage.py` so they stay visible rather than
forgotten.
**Deployment note.** `CREATE INDEX` takes a lock that blocks writes for the
duration. On DocQube's current row counts that is brief, but on a large
`drive_files` it will not be — run these with `CONCURRENTLY` outside a
transaction if the table has grown. Alembic cannot do that inside its own
transaction, which is why this migration is written to be easy to translate.
Revision ID: b6_4_index_review
Revises: b1_3_row_level_security
"""
from alembic import op
revision = "b6_4_index_review"
down_revision = "b1_3_row_level_security"
branch_labels = None
depends_on = None
# (index name, table, columns) — each justified by a query path measured in
# tests/characterization/test_query_budget.py.
INDEXES = [
# Every folder listing filters files by folder.
("ix_drive_files_folder_id", "drive_files", "folder_id"),
("ix_drive_files_owner_id", "drive_files", "owner_id"),
# Hierarchy walks in get_effective_role and breadcrumbs.
("ix_drive_folders_parent_id", "drive_folders", "parent_id"),
("ix_drive_folders_owner_id", "drive_folders", "owner_id"),
# The batched star lookup added in B6.3 filters on all three.
(
"ix_drive_stars_user_resource",
"drive_stars",
"user_id, resource_type, resource_id",
),
# Version history, loaded per file.
("ix_drive_file_versions_file_id", "drive_file_versions", "file_id"),
("ix_project_versions_project_id", "project_versions", "project_id"),
# Permission resolution joins accesses on every authenticated request.
("ix_role_accesses_access_id", "role_accesses", "access_id"),
# Signing looks requests up by the file they belong to.
("ix_signing_requests_drive_file_id", "signing_requests", "drive_file_id"),
# Comments and activity are read per resource.
("ix_drive_comments_author_id", "drive_comments", "author_id"),
("ix_drive_activities_actor_id", "drive_activities", "actor_id"),
]
def upgrade() -> None:
for name, table, columns in INDEXES:
op.execute(f"CREATE INDEX IF NOT EXISTS {name} ON {table} ({columns})")
def downgrade() -> None:
for name, _table, _columns in INDEXES:
op.execute(f"DROP INDEX IF EXISTS {name}")