You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adopt Alembic as the migration system for the PostgreSQL backend, replacing the SQLite-only bespoke runner (db/migrations.py: PRAGMA table_info + INSERT OR IGNORE + raw ALTER) and the fresh-build-from-schema.py path (init_schema_postgres). This is the migration story for the Postgres-only future — SQLite is being dropped, so we do not invest in dual-backend Alembic; SQLite keeps its legacy bespoke path until it is removed.
Long-term goal: db/tables.py SQLAlchemy MetaData becomes the single source of truth, ending the current 3-way hand-sync (schema.py CREATE strings / tables.py Core handles / migrations.py ALTERs). Relates to #746 (collapse schema.py + migrations.py), #1160 (migration runner data-loss / no cross-process serialization), #721 (schema drift), #300 (the Core abstraction this builds on).
Motivation
No in-place upgrade path for existing Postgres DBs today.init_schema_postgres only CREATE ... IF NOT EXISTS — a pre-existing PG database never picks up columns added later. Fresh-from-head only.
Alembic is SQLAlchemy-native: dialect-aware DDL, ordered revisions with down-migrations, autogenerate diffing live DB vs tables.py metadata, and a battle-tested version table + locking story.
Two migration domains (must preserve)
src/backend/enterprise/ is a separate (private) repo with its own migration system (backend/_migrations.py):
Separate tracking table enterprise_schema_migrations; owns only enterprise_* tables; never ALTERs an OSS table; runs from register_enterprise() AFTER OSS init_database.
Alembic adoption must keep the two domains isolated:
OSS revisions in this (public) repo, version table alembic_version.
Enterprise revisions in the private enterprise repo, separate version table (e.g. alembic_version_enterprise) / branch label, applied after OSS at startup. No enterprise DDL in this repo.
Phase 2 — Enterprise domain (private repo, separate PR)
Enterprise Alembic env with its own version table/branch, discovered + applied from register_enterprise() after OSS upgrade. Revisions live in the enterprise repo only.
Phase 3 — Single source of truth + SQLite retirement (later, with #746)
New schema changes authored as Alembic autogenerate revisions from tables.py metadata.
Retire schema.pyTABLES fresh-build + db/migrations.py once SQLite is dropped; collapse to tables.py + Alembic.
Interim workaround (accepted)
During the transition, a schema change may need both an Alembic revision (Postgres) and a legacy db/migrations.py entry (SQLite) until SQLite is removed. Acceptable short-term; tracked for cleanup in Phase 3.
A few existing bespoke migrations do data backfills and SQLite table-rebuilds (fake column-drop); those are SQLite-only and do not need porting (SQLite is going away). The Alembic baseline is the head schema, not a replay of history.
Summary
Adopt Alembic as the migration system for the PostgreSQL backend, replacing the SQLite-only bespoke runner (
db/migrations.py:PRAGMA table_info+INSERT OR IGNORE+ rawALTER) and the fresh-build-from-schema.pypath (init_schema_postgres). This is the migration story for the Postgres-only future — SQLite is being dropped, so we do not invest in dual-backend Alembic; SQLite keeps its legacy bespoke path until it is removed.Long-term goal:
db/tables.pySQLAlchemyMetaDatabecomes the single source of truth, ending the current 3-way hand-sync (schema.pyCREATE strings /tables.pyCore handles /migrations.pyALTERs). Relates to #746 (collapse schema.py + migrations.py), #1160 (migration runner data-loss / no cross-process serialization), #721 (schema drift), #300 (the Core abstraction this builds on).Motivation
init_schema_postgresonlyCREATE ... IF NOT EXISTS— a pre-existing PG database never picks up columns added later. Fresh-from-head only.INSERT OR IGNORE,sqlite3.OperationalError), has a DROP-rebuild data-loss window and no cross-process serialization (fix(db): migration runner — DROP-rebuild data-loss window and no cross-process serialization #1160), and forces triple-maintenance per schema change.autogeneratediffing live DB vstables.pymetadata, and a battle-tested version table + locking story.Two migration domains (must preserve)
src/backend/enterprise/is a separate (private) repo with its own migration system (backend/_migrations.py):enterprise_schema_migrations; owns onlyenterprise_*tables; never ALTERs an OSS table; runs fromregister_enterprise()AFTER OSSinit_database.versions/style (discover()+ module-prefixed names likesiem_0001_…).Alembic adoption must keep the two domains isolated:
alembic_version.alembic_version_enterprise) / branch label, applied after OSS at startup. No enterprise DDL in this repo.Acceptance Criteria (phased)
Phase 1 — OSS Alembic foundation (this repo)
alembicdependency (backend image + scheduler image).alembic.ini+migrations/env.pywired todb/tables.pymetadataastarget_metadata; offline + online modes; URL fromDATABASE_URL.alembic upgrade head, notinit_schema_postgres).init_database()Postgres branch runsalembic upgrade head(OSS) instead ofinit_schema_postgres; SQLite branch unchanged (legacy bespoke path retained).alembic stampa DB already at head-schema so it isn't rebuilt.upgrade headthendowngrade baseround-trips on a throwaway PG (CI dual-backend job /TEST_POSTGRES_URL).architecture.mdmigration section +docs/POSTGRESQL_SETUP.md.Phase 2 — Enterprise domain (private repo, separate PR)
register_enterprise()after OSS upgrade. Revisions live in the enterprise repo only.Phase 3 — Single source of truth + SQLite retirement (later, with #746)
tables.pymetadata.schema.pyTABLESfresh-build +db/migrations.pyonce SQLite is dropped; collapse totables.py+ Alembic.Interim workaround (accepted)
During the transition, a schema change may need both an Alembic revision (Postgres) and a legacy
db/migrations.pyentry (SQLite) until SQLite is removed. Acceptable short-term; tracked for cleanup in Phase 3.Technical Notes
MetaDatafromdb/tables.pyintroduced in Configurable database backend: SQLAlchemy Core abstraction (SQLite + PostgreSQL) #300 — it already lists every table.upgradeif needed (addresses fix(db): migration runner — DROP-rebuild data-loss window and no cross-process serialization #1160's serialization gap).