prefect server in zig
database migrations#
embedded migration system supporting sqlite and postgresql.
overview#
migrations are applied automatically on server startup. the system:
- tracks applied migrations in
_migrationstable - embeds SQL files in the binary via
@embedFile - supports dialect-specific SQL (sqlite vs postgres)
- is idempotent (safe to run repeatedly)
structure#
src/db/
├── migrations/
│ ├── 001_initial/
│ │ ├── sqlite.sql # sqlite DDL
│ │ └── postgres.sql # postgres DDL
│ └── 002_add_feature/ # future migrations
│ ├── sqlite.sql
│ └── postgres.sql
├── migrations_data.zig # migration registry
└── migrate.zig # migration runner
adding a new migration#
- create directory:
src/db/migrations/NNN_description/ - add
sqlite.sqlandpostgres.sqlwith your DDL - register in
migrations_data.zig:
pub const all = [_]Migration{
.{
.id = "001_initial",
.sqlite_sql = @embedFile("migrations/001_initial/sqlite.sql"),
.postgres_sql = @embedFile("migrations/001_initial/postgres.sql"),
},
.{
.id = "002_add_feature", // <-- add new entry
.sqlite_sql = @embedFile("migrations/002_add_feature/sqlite.sql"),
.postgres_sql = @embedFile("migrations/002_add_feature/postgres.sql"),
},
};
- rebuild:
zig build
migration sql guidelines#
- use
CREATE TABLE IF NOT EXISTSfor new tables - use
CREATE INDEX IF NOT EXISTSfor indexes - for schema changes, write dialect-specific SQL:
- sqlite: may need table recreation for some ALTER operations
- postgres: supports most ALTER operations directly
example migration adding a column:
sqlite.sql:
-- sqlite doesn't support ADD COLUMN IF NOT EXISTS, so we use a pragma check
ALTER TABLE flow_run ADD COLUMN new_field TEXT DEFAULT '';
postgres.sql:
ALTER TABLE flow_run ADD COLUMN IF NOT EXISTS new_field TEXT DEFAULT '';
tracking table#
CREATE TABLE IF NOT EXISTS _migrations (
id TEXT PRIMARY KEY, -- "001_initial"
applied_at TEXT NOT NULL -- ISO 8601 timestamp
);
runtime behavior#
on startup:
- creates
_migrationstable if not exists - queries applied migrations
- applies pending migrations in order
- records each applied migration
05:39:28.243 | INFO | migrations - applying: 001_initial
05:39:28.262 | INFO | migrations - applied 001_initial (44 statements)
on subsequent runs:
05:39:42.218 | DEBUG | migrations - skipping already applied: 001_initial
design decisions#
- manual registration: migrations must be explicitly added to
migrations_data.zig - no rollbacks: forward-only migrations (simplicity over flexibility)
- no transactions for DDL: sqlite auto-commits DDL; statements run sequentially
- statement splitting: SQL files can contain multiple statements separated by
;
future work#
--migrate-onlyCLI flag for CI/CD- migration generation tooling
- rollback support (if needed)
reference#
- python-reference/ - how prefect python handles migrations
- old schema files kept in
src/db/schema/for reference