Database Schema Migrations
We use Alembic to automate database schema migrations (e.g., adding a table, altering a column, and so on). Put simply: it allows us to specify database schema migrations using Python in a structured manner.
This document outlines the general workflow of using Alembic for the Metadata Catalogue. Examples of migration scripts can be found in the different schema subpages, e.g., "Changing Attributes". For other general usage of Alembic, please refer to its documentation.
Usage
We run alembic from a docker container, because it makes it convenient to connect to the docker network. Commands below assume that the root directory of the project is your current working directory.
First, build the image with:
docker build -f alembic/Dockerfile . -t aiod-migration
With the sqlserver container running (e.g., by using ./scripts/up.sh
, you can migrate to the latest schema with:
docker run -v $(pwd)/alembic:/alembic:ro -v $(pwd)/src:/app -it --network aiod-rest-api_default aiod-migration
--network
is the docker network that has the sqlserver
container.
The alembic directory is mounted to ensure the latest migrations are available,
the src directory is mounted so the migration scripts can use defined classes and variable from the project.
Update the Database
Database migrations may be irreversible!
Always make sure there is a backup of the old database.
Following the usage commands above, on a new release we should run Alembic to ensure the latest schema changes are applied. The default entrypoint of the container specifies to upgrade the database to the latest schema.
Updating the Database from Develop
During development, you will likely delete and recreate the database with some regularity. It is inevitable that your database schema will then be created according to the current SQLModels defined in the metadata catalogue. However, if you would attempt to run the Alembic migrations from scratch, they would no longer work since they assume a different initial schema.
Alembic stores information on the migration status in the database.
We can update the database entry to tell Alembic on which revision we are.
For example, to find out what revision develop
is on:
- Check out the development branch, and ensure there are no local changes (in
./alembic
). - Start docker compose, ensure the database is running, and start the alembic container with
--entrypoint=/bin/bash
. - Run
alembic head
to get the hash of the latest revision (e.g.,d09ed8ad4533
), or usealembic history
to find the right hash. - Run
alembic stamp HASH
whereHASH
is replaced with the hash from the previous step. - If you're sure you just need the latest version, you can do
alembic stamp heads
directly.
Adding a Revision
Build the docker image above, and start a container of it with shell as entry:
docker run -v $(pwd)/alembic:/alembic -v $(pwd)/src:/app -it --network aiod_default --entrypoint=/bin/bash aiod-migration
Then follow regular alembic
steps:
alembic revision -m "revision message"
Note that working from a docker container is not strictly necessary, but it helps set up the PYTHONPATH correctly, so that you can import from the src
directory.
TODO
- set up support for auto-generating migration scripts: https://alembic.sqlalchemy.org/en/latest/autogenerate.html
- fix the dockerfile so the generated image can directly run alembic