Skip to content

Commit b166e1f

Browse files
committed
Merge remote-tracking branch 'upstream/main'
# Conflicts: # invokeai/app/services/shared/sqlite/sqlite_util.py
2 parents b7af45d + b980f00 commit b166e1f

41 files changed

Lines changed: 1618 additions & 102 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/src/config/sidebar.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const sidebar: SidebarConfig = [
5555
collapsed: true,
5656
},
5757
{
58-
label: 'Troubleshooting',
58+
label: 'Troubleshooting & Help',
5959
items: [
6060
{
6161
autogenerate: { directory: 'troubleshooting' },
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
---
2+
title: SQLite Database Migrations
3+
lastUpdated: 2026-06-30
4+
---
5+
6+
InvokeAI uses a custom SQLite migrator for the main application database. Migrations live in `invokeai/app/services/shared/sqlite_migrator/migrations/` and are discovered automatically when the database is initialized.
7+
8+
Use a migration when a change modifies persisted database schema or persisted database data in a way that existing installs must receive on startup.
9+
10+
## Naming
11+
12+
New migration modules should use a date-stamped descriptive name:
13+
14+
```text
15+
invokeai/app/services/shared/sqlite_migrator/migrations/migration_2026_06_30_add_example_table.py
16+
```
17+
18+
Date-stamped migration modules must expose a `build_migration()` builder:
19+
20+
```py
21+
def build_migration() -> Migration:
22+
...
23+
```
24+
25+
The date prefix should be the date the migration is authored or merged, in `YYYY_MM_DD` format. Add a short snake_case description after the date.
26+
27+
Legacy numeric migration modules are still supported:
28+
29+
```text
30+
invokeai/app/services/shared/sqlite_migrator/migrations/migration_33.py
31+
```
32+
33+
Numeric migration modules must expose the matching legacy builder:
34+
35+
```py
36+
def build_migration_33() -> Migration:
37+
...
38+
```
39+
40+
The loader discovers both naming styles. Numeric modules are imported in numeric order for legacy compatibility. Date-stamped modules are imported in lexical order, but dependency metadata drives execution order. Non-matching migration module names, missing builders, unknown builder dependencies, and builders that do not return `Migration` fail at startup.
41+
42+
## IDs and Dependencies
43+
44+
Every migration has a stable `id`. For new date-stamped migrations, use the module name without the `migration_` prefix:
45+
46+
```py
47+
id="2026_06_30_add_example_table"
48+
```
49+
50+
The loader enforces this match. If `migration_2026_06_30_add_example_table.py` returns a different ID, startup fails instead of persisting a typo that could later cause the migration to run again.
51+
52+
Existing numeric migrations use IDs matching their file number:
53+
54+
```py
55+
id="migration_33"
56+
```
57+
58+
Legacy numeric migrations that define `from_version` and `to_version` may only depend on other legacy numeric migrations. New migrations should usually be date-stamped graph-only migrations, and should not define legacy versions unless a specific legacy compatibility requirement exists.
59+
60+
For a migration that only needs an older schema point, depend on the migration that introduced the required schema or data:
61+
62+
```py
63+
depends_on="migration_27"
64+
```
65+
66+
New production migrations should always set `depends_on`. Only a true root migration should use `depends_on=None`. A migration with no dependency is considered independently runnable, so omitting `depends_on` for a normal schema or data change can allow it to run before the schema it expects exists.
67+
68+
Dependencies drive execution order. If two migrations both depend on `migration_27`, either may run after `migration_27` unless one explicitly depends on the other. Do not rely on filename or lexical ordering to express a real dependency.
69+
70+
Single-parent dependencies are currently supported. If a migration truly requires two independent branches, add a real dependency between those branches only when that ordering is semantically correct. Otherwise, update the migrator design and tests to support multiple dependencies.
71+
72+
## Template
73+
74+
```py
75+
import sqlite3
76+
77+
from invokeai.app.services.shared.sqlite_migrator.sqlite_migrator_common import Migration
78+
79+
80+
class AddExampleTableCallback:
81+
def __call__(self, cursor: sqlite3.Cursor) -> None:
82+
cursor.execute("ALTER TABLE images ADD COLUMN example TEXT;")
83+
84+
85+
def build_migration() -> Migration:
86+
return Migration(
87+
id="2026_06_30_add_example_table",
88+
depends_on="migration_32",
89+
callback=AddExampleTableCallback(),
90+
)
91+
```
92+
93+
Keep the callback self-contained. It receives an open SQLite cursor and must not commit or roll back the transaction. The migrator commits the migration and records it as applied only after the callback succeeds. If the callback raises, the migration transaction is rolled back and the migration is not recorded.
94+
95+
## Builder Dependencies
96+
97+
Migration builders may request known application dependencies by parameter name. The loader inspects the builder signature and passes only the dependencies requested.
98+
99+
Supported dependency names:
100+
101+
- `app_config` or `config`
102+
- `logger`
103+
- `image_files`
104+
105+
Example:
106+
107+
```py
108+
def build_migration(app_config: InvokeAIAppConfig, logger: Logger) -> Migration:
109+
return Migration(
110+
id="2026_06_30_normalize_model_paths",
111+
depends_on="2026_06_30_add_example_table",
112+
callback=NormalizeModelPathsCallback(app_config=app_config, logger=logger),
113+
)
114+
```
115+
116+
Do not use `*args`, `**kwargs`, or positional-only parameters in migration builders.
117+
118+
## Registration
119+
120+
Do not manually import or register migrations in `sqlite_util.py`.
121+
122+
Database initialization builds a `MigrationBuildContext`, discovers migration modules, calls their builders, and registers the resulting `Migration` objects with `SqliteMigrator`.
123+
124+
Manual registration is still available for tests:
125+
126+
```py
127+
migrator.register_migration(Migration(...))
128+
```
129+
130+
## Applied State and Compatibility
131+
132+
The migrator records stable migration IDs in the `applied_migrations` table. Legacy numeric versions are still written to the existing `migrations` table for migrations that define `to_version`.
133+
134+
New date-stamped migrations should not define `from_version` or `to_version` unless there is a specific legacy compatibility reason. Use `id` and `depends_on` to define execution.
135+
136+
Existing databases are bootstrapped from legacy numeric rows:
137+
138+
- legacy version `1` maps to `migration_1`
139+
- legacy version `2` maps to `migration_2`
140+
- and so on
141+
142+
If a database contains an applied migration ID or legacy numeric version unknown to the current code, startup fails before running migrations. This prevents older code from running against a newer schema. Downgrading to an InvokeAI version that does not know about migrations already applied by a newer version is not supported.
143+
144+
For legacy migrations, the two metadata tables must agree. For example, `applied_migrations` may only record `migration_2` with `legacy_version = 2` if the legacy `migrations` table also contains version `2`. If these records are inconsistent, startup fails before user migration callbacks run.
145+
146+
When opening a file-backed legacy database for the first time after this migration system change, the migrator may need to create and populate `applied_migrations` even if no user migration callbacks need to run. This metadata bootstrap is backed up before the new metadata table is written.
147+
148+
## Tests
149+
150+
Add focused tests for each migration callback under:
151+
152+
```text
153+
tests/app/services/shared/sqlite_migrator/migrations/
154+
```
155+
156+
At minimum, cover:
157+
158+
- the schema or data change performed by the callback
159+
- idempotent or already-migrated behavior when relevant
160+
- missing optional source tables or columns when the migration is expected to tolerate them
161+
- the builder's `id` and `depends_on`
162+
- legacy `from_version` and `to_version`, if the migration defines them
163+
164+
Also run the migrator tests:
165+
166+
```bash
167+
pytest tests/app/services/shared/sqlite_migrator tests/test_sqlite_migrator.py
168+
```
169+
170+
These tests use in-memory databases or pytest temporary directories. They should not touch a real InvokeAI database.

0 commit comments

Comments
 (0)