db:upgrade
Table of contents
- Introduction
- Upgrading one feature
- The order, and what it retries
- Two replicas at once
- A migration that needs a decision
- Reference
- Requirements
Introduction
db:upgrade applies the pending migrations of every feature, or of one. It never writes new scripts. Run it after you pull code, or after db:migrate wrote new files.
splent db:upgrade
splent_feature_notes -> 990210b54b52
splent_feature_reports -> 126da35a357f
Each applied feature prints its new revision, records it in splent_migrations and moves to migrated. A declared feature that ships no migrations moves to migrated too, since it has nothing to apply.
Upgrading one feature
splent db:upgrade splent_feature_notes
A feature can be named notes, splent_feature_notes, splent_feature_notes@v1.2.0 or splent-io/splent_feature_notes@v1.2.0. For a feature that ships no migrations it prints <feature> has no migrations, nothing to apply. and exits 0.
The order, and what it retries
Features migrate in the UVL dependency order that feature:order shows, each after the features it depends on and every refiner after its base. The order in pyproject.toml plays no part.
A migration may still reference the table of a feature it does not depend on, so on a fresh database it can fail first. Then you see retrying <features> now that the rest has migrated, and each new pass retries what failed while the last one applied something. What still fails ends the run with Error: Migration upgrade failed for: <features>.
Two replicas at once
The whole upgrade holds a named lock of the database server, GET_LOCK('splent_migrate:<database>') on MariaDB. When two replicas start together, the second waits and then finds nothing to apply.
another db:upgrade is migrating this database, waiting for it (lock splent_migrate:<database>)...
It waits up to --lock-timeout seconds, 600 by default, and then exits 1 having migrated nothing. A database without named locks, such as SQLite, runs without one.
The product’s start passes --fresh-marker /tmp/splent_fresh_db. The file is created when the database had no table but splent_migrations, decided under the lock, so only the replica that migrates an empty database seeds it. See Migrating when the product starts.
A migration that needs a decision
Some migrations need a value only you know. Say notes0002_visibility adds a visibility column, and the notes already there need one. With notes in the table and NOTES_MIGRATION_LEGACY_VISIBILITY empty, it stops before it touches the table.
notes0002_visibility found 3 existing notes and NOTES_MIGRATION_LEGACY_VISIBILITY is empty, so it does not know who may read them. Declare NOTES_MIGRATION_LEGACY_VISIBILITY in the product's [tool.splent.config]. Then bring it into the environment this migration reads: in development run splent product:env --merge --dev and then splent db:upgrade again, in production rebuild and deploy, and the container applies the migration when it starts.
In development, declare the value, merge it into docker/.env and migrate again. No restart is needed.
[tool.splent.config]
NOTES_MIGRATION_LEGACY_VISIBILITY = "private"
splent product:env --merge --dev
splent db:upgrade
In production, declare it the same way, rebuild with product:build and deploy with product:deploy. The container migrates when it starts. On a host that runs the bundle without SPLENT, add the key to deploy/.env by hand, where it is kept.
Reference
Options
| Option | Description |
|---|---|
FEATURE |
Optional. Only this feature |
--fresh-marker PATH |
Create this file when the database was empty before the upgrade, and remove it when it was not |
--lock-timeout SECONDS |
How long to wait for another db:upgrade on the same database. 600 by default |
Errors
| Message | What to do |
|---|---|
❌ <feature> is out of sync with the database. |
The database is at a revision the installed code lacks, usually after pinning an older release. Take one of the ways out it lists, feature:unlock, db:rollback --to-base or db:reset |
Error: Another db:upgrade held the migration lock ... |
Find the connection with SELECT IS_USED_LOCK('splent_migrate:<database>') and SHOW PROCESSLIST |
❌ No migrations directory found for '<feature>'. |
The feature is not declared, or has no migrations/ folder |
Error: Cannot read the features of <product>/pyproject.toml: <reason> |
Fix the TOML, or the feature list it names |
A feature with migrations/ and no models module is skipped silently.
Requirements
- A selected product (
SPLENT_APP), and a reachable database. - Migration scripts in each feature’s
migrations/versions/.