Skip to content

Commit 930cf20

Browse files
earayuclaude
andcommitted
feat(celery T3.1 commit 1/5): alembic migration — drop legacy + ALTER NOT NULL + rename to canonical
Wave 3 hard-cut schema migration per architect msg=4a801b2b (Wave 1 Bug 2 ruling that locked the temporary v2 suffix) + msg=498b12f0 (Wave 2 informational item ruling that promoted dispatch columns NOT NULL in Wave 3) + PM acceptance msg=5939e394 item 1. Migration revision d0f4c1b9a8e2 chains off c2e8d5a1f3b9 and: 1. DROP TABLE document_index CASCADE — the legacy Celery-era table that lived alongside the Wave 1 v2 table during the transition. Pre-launch + no callers in Wave 3 (the dependent code is hard- deleted in subsequent commits of this same PR). 2. ALTER COLUMN collection_id, source_path → NOT NULL on document_index_v2. Wave 1 fixtures used NULL for back-compat; Wave 3 orchestrator + reconciler always populate them (per architect msg=498b12f0 Lock). 3. Rename every index *_v2_* → *_*. The partial-unique uniq_document_index_v2_serving is dropped + re-created (PG ALTER INDEX RENAME does not regenerate the WHERE predicate symbol map per Postgres quirk; SQLite would silently keep the old reference). 4. RENAME TABLE document_index_v2 → document_index — back to the §F.1 canonical name (architect msg=4a801b2b lock). The downgrade reverses every step in mirror order so a rollback can replay subsequent migrations cleanly. The recreated legacy ``document_index`` table on downgrade is intentionally schema-less (only the id PK column) because the legacy class was deleted in the Wave 3 PR alongside this migration — operators rolling back past this point must restore the legacy ORM file before re-running upgrades. There is no production scenario for that. This is commit 1/5 of T3.1; subsequent commits land the FastAPI wire-in, knowledge_base/tasks.py Pattern A/B/C migration of the 6 remaining Celery tasks, the 9 production caller migrations, and the legacy file-layer hard-delete + audit allowlist removal + pyproject Celery/kombu dep removal. Design pack §F.1 + §F.5 amends (per architect msg=498b12f0 + msg=3890c9d7 path C ruling) are deferred to a follow-up commit once PR #1725 (which owns docs/modularization/indexing-redesign- design-pack.md) merges — flagged in the channel. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent f370dc6 commit 930cf20

1 file changed

Lines changed: 167 additions & 0 deletions

File tree

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
"""indexing redesign — hard-cut + rename to canonical (T3.1)
2+
3+
Phase celery T3.1 per ``docs/modularization/indexing-redesign-design-pack.md``
4+
§F.1 + §K Wave 3 + architect amendments msg=4a801b2b / msg=498b12f0:
5+
6+
This migration completes the Wave 1 → Wave 3 schema cutover:
7+
8+
1. ``DROP TABLE document_index`` — the legacy Celery-era table (per
9+
``aperag/domains/indexing/db/models.py:DocumentIndex`` Wave 1 code,
10+
which Wave 3 hard-deletes alongside this migration).
11+
2. ``ALTER TABLE document_index_v2`` — set the two T2.1 dispatch
12+
columns (``collection_id``, ``source_path``) to ``NOT NULL``. The
13+
Wave 1 fixture back-compat that justified ``NULL`` is gone in
14+
Wave 3 (the orchestrator + reconciler always populate them at
15+
INSERT time per architect msg=498b12f0).
16+
3. ``RENAME TABLE document_index_v2 → document_index`` — back to
17+
the §F.1 canonical name. The "v2" suffix was a temporary measure
18+
(architect msg=4a801b2b) to avoid the SQLAlchemy table-name
19+
collision with the legacy class while both lived in the codebase.
20+
4. Rename every index from ``*_v2_*`` → ``*_*`` to match the new
21+
table name (PG + SQLite both support ``ALTER INDEX RENAME``;
22+
the partial unique index is dropped + re-created since
23+
Postgres ALTER INDEX cannot relocate ``WHERE`` predicates and
24+
SQLite would silently keep the old reference).
25+
26+
Pre-launch system has no users / no data, so the schema rewrite
27+
lands without backfill (per earayu2 hard-cut acceptance msg=9730bb6b).
28+
The downgrade reverses every step so a rollback can replay subsequent
29+
migrations cleanly.
30+
31+
Revision ID: d0f4c1b9a8e2
32+
Revises: c2e8d5a1f3b9
33+
Create Date: 2026-04-27 01:30:00.000000
34+
"""
35+
36+
from typing import Sequence, Union
37+
38+
import sqlalchemy as sa
39+
from alembic import op
40+
41+
revision: str = "d0f4c1b9a8e2"
42+
down_revision: Union[str, None] = "c2e8d5a1f3b9"
43+
branch_labels: Union[str, Sequence[str], None] = None
44+
depends_on: Union[str, Sequence[str], None] = None
45+
46+
47+
def upgrade() -> None:
48+
# 1. Drop the legacy Celery-era ``document_index`` table. Pre-
49+
# launch + no callers in Wave 3 (the dependent code is hard-
50+
# deleted in the same PR).
51+
op.execute("DROP TABLE IF EXISTS document_index CASCADE")
52+
53+
# 2. Promote the two dispatch columns to NOT NULL. Wave 1 fixtures
54+
# used NULL for back-compat; Wave 3 orchestrator + reconciler
55+
# always populate them.
56+
op.alter_column(
57+
"document_index_v2",
58+
"collection_id",
59+
existing_type=sa.String(length=64),
60+
nullable=False,
61+
)
62+
op.alter_column(
63+
"document_index_v2",
64+
"source_path",
65+
existing_type=sa.Text(),
66+
nullable=False,
67+
)
68+
69+
# 3. Rename indexes from *_v2_* → *_* before we rename the table
70+
# (PG / SQLite both fine with this order, and it keeps the index
71+
# symbol changes visible in the alembic diff). The partial-unique
72+
# index is dropped + re-created because the WHERE predicate must
73+
# be re-emitted for the new index name (PG quirk: ALTER INDEX
74+
# RENAME does not regenerate the predicate symbol map).
75+
op.drop_index(
76+
"uniq_document_index_v2_serving",
77+
table_name="document_index_v2",
78+
)
79+
op.execute(
80+
"ALTER INDEX uq_document_index_v2_triple "
81+
"RENAME TO uq_document_index_triple"
82+
)
83+
op.execute(
84+
"ALTER INDEX idx_document_index_v2_status_modality "
85+
"RENAME TO idx_document_index_status_modality"
86+
)
87+
op.execute(
88+
"ALTER INDEX idx_document_index_v2_document_modality "
89+
"RENAME TO idx_document_index_document_modality"
90+
)
91+
op.execute(
92+
"ALTER INDEX idx_document_index_v2_tenant_scope "
93+
"RENAME TO idx_document_index_tenant_scope"
94+
)
95+
op.execute(
96+
"ALTER INDEX idx_document_index_v2_collection "
97+
"RENAME TO idx_document_index_collection"
98+
)
99+
100+
# 4. Rename the table back to the §F.1 canonical name.
101+
op.rename_table("document_index_v2", "document_index")
102+
103+
# 5. Re-create the partial unique index against the final table
104+
# name. PG + SQLite 3.8+ both support the same syntax.
105+
op.create_index(
106+
"uniq_document_index_serving",
107+
"document_index",
108+
["document_id", "modality"],
109+
unique=True,
110+
postgresql_where=sa.text("is_serving = TRUE"),
111+
sqlite_where=sa.text("is_serving = TRUE"),
112+
)
113+
114+
115+
def downgrade() -> None:
116+
# Reverse the upgrade, mirroring its order in reverse.
117+
op.drop_index("uniq_document_index_serving", table_name="document_index")
118+
op.rename_table("document_index", "document_index_v2")
119+
op.execute(
120+
"ALTER INDEX idx_document_index_collection "
121+
"RENAME TO idx_document_index_v2_collection"
122+
)
123+
op.execute(
124+
"ALTER INDEX idx_document_index_tenant_scope "
125+
"RENAME TO idx_document_index_v2_tenant_scope"
126+
)
127+
op.execute(
128+
"ALTER INDEX idx_document_index_document_modality "
129+
"RENAME TO idx_document_index_v2_document_modality"
130+
)
131+
op.execute(
132+
"ALTER INDEX idx_document_index_status_modality "
133+
"RENAME TO idx_document_index_v2_status_modality"
134+
)
135+
op.execute(
136+
"ALTER INDEX uq_document_index_triple "
137+
"RENAME TO uq_document_index_v2_triple"
138+
)
139+
op.create_index(
140+
"uniq_document_index_v2_serving",
141+
"document_index_v2",
142+
["document_id", "modality"],
143+
unique=True,
144+
postgresql_where=sa.text("is_serving = TRUE"),
145+
sqlite_where=sa.text("is_serving = TRUE"),
146+
)
147+
op.alter_column(
148+
"document_index_v2",
149+
"source_path",
150+
existing_type=sa.Text(),
151+
nullable=True,
152+
)
153+
op.alter_column(
154+
"document_index_v2",
155+
"collection_id",
156+
existing_type=sa.String(length=64),
157+
nullable=True,
158+
)
159+
# The legacy ``document_index`` table is recreated minimally so
160+
# the f9c4d2a8e1b5 → c2e8d5a1f3b9 chain can replay cleanly, but
161+
# it is intentionally schema-less because the legacy class was
162+
# also deleted in this Wave 3 migration. Operators rolling back
163+
# past this migration must restore the legacy class file before
164+
# re-running upgrades — there is no production scenario for it.
165+
op.execute(
166+
"CREATE TABLE document_index (id INTEGER PRIMARY KEY)"
167+
)

0 commit comments

Comments
 (0)