Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ repos:
additional_dependencies:
- aiopg==1.4.0
- alabaster==1.0.0
- alembic==1.18.4
- asgiref==3.11.0
- async-timeout==4.0.3
- attrs==25.4.0
Expand All @@ -53,6 +54,7 @@ repos:
- idna==3.15
- imagesize==1.4.1
- jinja2==3.1.6
- mako==1.3.12
- markupsafe==3.0.3
- packaging==26.0
- psycopg==3.3.2
Expand Down
37 changes: 37 additions & 0 deletions docs/howto/production/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ ALTER TABLE procrastinate_jobs ADD COLUMN extra TEXT;
The migration scripts are pure-SQL scripts, meaning that they may be applied to the
database using any PostgreSQL client, including `psql` and `PGAdmin`.

Procrastinate also ships optional Alembic revisions that wrap the same SQL
migration scripts. Install them with:

```console
pip install "procrastinate[alembic]"
```

:::{note}
If you use Django, instead of using the SQL migration scripts directly, you way want
to rely on the Procrastinate Django app, and the Django database migration scripts
Expand All @@ -37,6 +44,36 @@ $ procrastinate schema --migrations-path
/home/me/my_venv/lib/python3.x/lib/site-packages/procrastinate/sql/migrations
```

If your project already uses Alembic, add Procrastinate's packaged Alembic
versions directory to your Alembic `version_locations`, then run Alembic normally:

```ini
[alembic]
version_locations = %(here)s/versions procrastinate:alembic/versions
```

The `procrastinate:alembic/versions` entry is resolved by Alembic from the
installed Python package, so it does not depend on where your virtual environment
or site-packages directory is located.

The Procrastinate Alembic tree uses revision IDs prefixed with `procrastinate_`
and a `procrastinate` branch label, so it can live alongside your own revisions.
Most projects should keep the Procrastinate and application revision trees
independent. If your own schema changes must run after a specific Procrastinate
revision, your revision may set `down_revision` to that Procrastinate revision.

Each Alembic revision wraps exactly one SQL migration script: the revision ID
(e.g. `procrastinate_0036`) is sequential, and each revision file records the
name of the SQL script it wraps. For blue-green deployments, upgrade to the
last `pre` revision of the target version before deploying, then to the last
`post` revision after:

```console
alembic upgrade procrastinate_0036
yoursystem/deploy procrastinate 3.4.0
alembic upgrade procrastinate_0037
```

It's your responsibility to keep track of which migrations have been applied yet
or not. Thankfully, the names of procrastinate migrations should help you: they
follow a specific pattern:
Expand Down
1 change: 1 addition & 0 deletions procrastinate/alembic/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from __future__ import annotations
1 change: 1 addition & 0 deletions procrastinate/alembic/versions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from __future__ import annotations
35 changes: 35 additions & 0 deletions procrastinate/alembic/versions/procrastinate_0000_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""00.00.00 01 initial."""

from __future__ import annotations

from importlib import resources

import sqlalchemy as sa
from alembic import op

revision = "procrastinate_0000"
down_revision: str | None = None
branch_labels = ("procrastinate",)
depends_on = None

MIGRATION_FILE = "00.00.00_01_initial.sql"


def _migration_sql() -> sa.TextClause:
sql = (
resources.files("procrastinate.sql.migrations")
.joinpath(MIGRATION_FILE)
.read_text(encoding="utf-8")
)
return sa.text(sql.replace(":", r"\:"))


def upgrade() -> None:
with op.get_context().autocommit_block():
op.execute(_migration_sql())


def downgrade() -> None:
raise NotImplementedError(
"Procrastinate Alembic revisions wrap irreversible SQL migrations."
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""00.05.00 01 drop started at column."""

from __future__ import annotations

from importlib import resources

import sqlalchemy as sa
from alembic import op

revision = "procrastinate_0001"
down_revision: str | None = "procrastinate_0000"
branch_labels = None
depends_on = None

MIGRATION_FILE = "00.05.00_01_drop_started_at_column.sql"


def _migration_sql() -> sa.TextClause:
sql = (
resources.files("procrastinate.sql.migrations")
.joinpath(MIGRATION_FILE)
.read_text(encoding="utf-8")
)
return sa.text(sql.replace(":", r"\:"))


def upgrade() -> None:
with op.get_context().autocommit_block():
op.execute(_migration_sql())


def downgrade() -> None:
raise NotImplementedError(
"Procrastinate Alembic revisions wrap irreversible SQL migrations."
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""00.05.00 02 drop started at column."""

from __future__ import annotations

from importlib import resources

import sqlalchemy as sa
from alembic import op

revision = "procrastinate_0002"
down_revision: str | None = "procrastinate_0001"
branch_labels = None
depends_on = None

MIGRATION_FILE = "00.05.00_02_drop_started_at_column.sql"


def _migration_sql() -> sa.TextClause:
sql = (
resources.files("procrastinate.sql.migrations")
.joinpath(MIGRATION_FILE)
.read_text(encoding="utf-8")
)
return sa.text(sql.replace(":", r"\:"))


def upgrade() -> None:
with op.get_context().autocommit_block():
op.execute(_migration_sql())


def downgrade() -> None:
raise NotImplementedError(
"Procrastinate Alembic revisions wrap irreversible SQL migrations."
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""00.05.00 03 drop procrastinate version table."""

from __future__ import annotations

from importlib import resources

import sqlalchemy as sa
from alembic import op

revision = "procrastinate_0003"
down_revision: str | None = "procrastinate_0002"
branch_labels = None
depends_on = None

MIGRATION_FILE = "00.05.00_03_drop_procrastinate_version_table.sql"


def _migration_sql() -> sa.TextClause:
sql = (
resources.files("procrastinate.sql.migrations")
.joinpath(MIGRATION_FILE)
.read_text(encoding="utf-8")
)
return sa.text(sql.replace(":", r"\:"))


def upgrade() -> None:
with op.get_context().autocommit_block():
op.execute(_migration_sql())


def downgrade() -> None:
raise NotImplementedError(
"Procrastinate Alembic revisions wrap irreversible SQL migrations."
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""00.06.00 01 fix procrastinate fetch job."""

from __future__ import annotations

from importlib import resources

import sqlalchemy as sa
from alembic import op

revision = "procrastinate_0004"
down_revision: str | None = "procrastinate_0003"
branch_labels = None
depends_on = None

MIGRATION_FILE = "00.06.00_01_fix_procrastinate_fetch_job.sql"


def _migration_sql() -> sa.TextClause:
sql = (
resources.files("procrastinate.sql.migrations")
.joinpath(MIGRATION_FILE)
.read_text(encoding="utf-8")
)
return sa.text(sql.replace(":", r"\:"))


def upgrade() -> None:
with op.get_context().autocommit_block():
op.execute(_migration_sql())


def downgrade() -> None:
raise NotImplementedError(
"Procrastinate Alembic revisions wrap irreversible SQL migrations."
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""00.07.01 01 fix trigger status events insert."""

from __future__ import annotations

from importlib import resources

import sqlalchemy as sa
from alembic import op

revision = "procrastinate_0005"
down_revision: str | None = "procrastinate_0004"
branch_labels = None
depends_on = None

MIGRATION_FILE = "00.07.01_01_fix_trigger_status_events_insert.sql"


def _migration_sql() -> sa.TextClause:
sql = (
resources.files("procrastinate.sql.migrations")
.joinpath(MIGRATION_FILE)
.read_text(encoding="utf-8")
)
return sa.text(sql.replace(":", r"\:"))


def upgrade() -> None:
with op.get_context().autocommit_block():
op.execute(_migration_sql())


def downgrade() -> None:
raise NotImplementedError(
"Procrastinate Alembic revisions wrap irreversible SQL migrations."
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""00.08.01 01 add queueing lock column."""

from __future__ import annotations

from importlib import resources

import sqlalchemy as sa
from alembic import op

revision = "procrastinate_0006"
down_revision: str | None = "procrastinate_0005"
branch_labels = None
depends_on = None

MIGRATION_FILE = "00.08.01_01_add_queueing_lock_column.sql"


def _migration_sql() -> sa.TextClause:
sql = (
resources.files("procrastinate.sql.migrations")
.joinpath(MIGRATION_FILE)
.read_text(encoding="utf-8")
)
return sa.text(sql.replace(":", r"\:"))


def upgrade() -> None:
with op.get_context().autocommit_block():
op.execute(_migration_sql())


def downgrade() -> None:
raise NotImplementedError(
"Procrastinate Alembic revisions wrap irreversible SQL migrations."
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""00.10.00 01 close fetch job race condition."""

from __future__ import annotations

from importlib import resources

import sqlalchemy as sa
from alembic import op

revision = "procrastinate_0007"
down_revision: str | None = "procrastinate_0006"
branch_labels = None
depends_on = None

MIGRATION_FILE = "00.10.00_01_close_fetch_job_race_condition.sql"


def _migration_sql() -> sa.TextClause:
sql = (
resources.files("procrastinate.sql.migrations")
.joinpath(MIGRATION_FILE)
.read_text(encoding="utf-8")
)
return sa.text(sql.replace(":", r"\:"))


def upgrade() -> None:
with op.get_context().autocommit_block():
op.execute(_migration_sql())


def downgrade() -> None:
raise NotImplementedError(
"Procrastinate Alembic revisions wrap irreversible SQL migrations."
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""00.10.00 02 add defer job function."""

from __future__ import annotations

from importlib import resources

import sqlalchemy as sa
from alembic import op

revision = "procrastinate_0008"
down_revision: str | None = "procrastinate_0007"
branch_labels = None
depends_on = None

MIGRATION_FILE = "00.10.00_02_add_defer_job_function.sql"


def _migration_sql() -> sa.TextClause:
sql = (
resources.files("procrastinate.sql.migrations")
.joinpath(MIGRATION_FILE)
.read_text(encoding="utf-8")
)
return sa.text(sql.replace(":", r"\:"))


def upgrade() -> None:
with op.get_context().autocommit_block():
op.execute(_migration_sql())


def downgrade() -> None:
raise NotImplementedError(
"Procrastinate Alembic revisions wrap irreversible SQL migrations."
)
Loading
Loading