Migrations¶
How the database schema is actually created and changed in this project.
Overview¶
This project does not use Alembic migrations. The schema is created directly from the SQLAlchemy models:
- At runtime:
backend/core/database.py(init_db, ~:390-415) runsModelsBase.metadata.create_allinside an advisory-lock-protectedengine.begin()block, so concurrent pytest-xdist workers / app processes don't race on schema creation. - From the CLI:
python -m backend.scripts.init_schema(backend/scripts/init_schema.py) drops all tables and recreates them from the models. NEM-4482 safeguards make it refuse to run against a production-lookingDATABASE_URL/ENVIRONMENTunless--forceis passed.
The Alembic tree lived at backend/alembic/ until PR #4465 (refactor(db): flatten database schema by removing alembic migrations) removed it. The alembic package is still installed as a dev dependency only ("Database migrations testing", pyproject.toml:118), and backend/tests/integration/test_alembic_migrations.py is a skipped placeholder suite for a future Alembic adoption.
Changing the Schema¶
The workflow is model-first:
- Edit the SQLAlchemy model in
backend/models/. - Development databases pick the change up via
create_allon next startup — butcreate_allonly adds new tables/columns-safe operations; it never alters existing columns, types, or indexes. - For destructive changes (column rename, type change, constraint), apply them by hand against the dev DB (
psql/podman exec postgres psql …), or recreate the database withpython -m backend.scripts.init_schema. - Partitioned tables (
events,detections,logs,gpu_stats) additionally need their partition maintenance — seebackend/scripts/partition jobs — becausecreate_allcreates the parent, not future monthly partitions.
Recreating the Schema (Development)¶
# Recreate all tables from models (destructive — prompts, refuses on prod URLs)
uv run python -m backend.scripts.init_schema
# Skip the confirmation prompt (still refuses production-looking URLs)
uv run python -m backend.scripts.init_schema --force
Integration tests do not use this script — each pytest-xdist worker creates its own security_test_gwN database and lets init_db's create_all build the schema (see Testing: Integration).
Reference: Historical Alembic Patterns¶
Pre-#4465 workflow (historical — no tooling exists to run this)
Before the flattening, the repo carried `backend/alembic/env.py` plus `versions/` migrations (`968b0dff6a9b_initial_schema.py`, `add_zones_table.py`, `fix_datetime_timezone.py`, …) and the workflow was: Migration modules followed the usual `upgrade()`/`downgrade()` patterns (creating tables with indexes, enum types, GIN indexes for JSONB, BRIN indexes on time columns, partial indexes, table/constraint renames, columns with defaults, junction tables, SAVEPOINT-guarded queries).Related Documentation¶
- Data Model Overview
- Core Entities
- Indexes and Performance
- Integration Testing - Per-worker schema creation