Repository navigation
feat(db): safe database upgrades with named steps, a lock, a backup first and lens migrations - #113
Conversation
…rations Data upgrades were tracked by a position counter, ran in every starting process at once, and left no record. Steps now have names (adopting the two the counter tracked), one process runs them under a lease lock while the others wait, each run or failure is kept in migration:<name>, and lens migrations lists them without running anything. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CMvFKqsrnJrxxvME87LRk7
A database with data is exported (server) or copied with it closed (embedded) into <data_dir>/backups before pending upgrade steps run; the newest three are kept. A failed backup stops the upgrade unless LENS_UPGRADE_BACKUP=off. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CMvFKqsrnJrxxvME87LRk7
…ain) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CMvFKqsrnJrxxvME87LRk7
|
Generated by Claude Code |
|
Generated by Claude Code |
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CMvFKqsrnJrxxvME87LRk7
… tests The self-hosted runner keeps containers between jobs, so docker run --name surreal failed on main as well. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CMvFKqsrnJrxxvME87LRk7
|
The Generated by Claude Code |
|
Update: #112 fixed both on main. I merged main into this PR, which also takes #112's version of the SurrealDB container steps in Generated by Claude Code |
…y5img5 # Conflicts: # .github/workflows/ci.yml
Requested by Adeel · project thread
Description
Before: data upgrades were a list in
store.migrate(), tracked by one counter (seq:migrations). Every process that opened the database (API, workers,lens watch) ran pending steps at the same moment, nothing was backed up first, nothing recorded what ran or why it failed, and two branches each adding a step could leave one skipped on a database.After: upgrades are named steps in
app/domain/migrations.py. On start, one process takes a lease lock (migration_lock:run, renewed by a heartbeat, lapses after 5 minutes if the holder dies) and the others wait. If the database holds data (any recording or account), the holder backs it up into<data_dir>/backups/before-upgrade-<time>first (server: SurrealDB's/export, gzipped; embedded: a copy of the database folder taken with it closed; newest three kept). A failed backup stops the upgrade with a clear message unlessLENS_UPGRADE_BACKUP=off. Then the steps run in order, each run or failure kept inmigration:⟨name⟩.New commands:
lens migrationslists done, pending, failed and unknown (run by a newer Lens) steps without upgrading, so it works while an upgrade is failing;--runruns them.lens backuptakes a backup on demand.Existing installs: the two steps the old counter tracked (
access-levels,collection-homes) are adopted fromseq:migrations, so nothing runs again and no backup is taken on this update.How:
store.connect(cfg, upgrade=True)callsmigrations.run(). New steps are appended tosteps()and toSHIPPEDin the test, which fails on any rename, reorder or removal.test_every_step_is_safe_to_repeatsnapshots every table after one run and checks a second run changes nothing. Backup tests restore the backup (into a fresh server database, or by opening the copied folder) and check it holds the pre-upgrade data. The convention is in docs/database.md (Upgrades, Backups) and CONTRIBUTING.md.Motivation and Context
Adeel asked how migrations work as we develop and to get them sorted. Users have real data on installs that update by running
install.shagain.Types of changes
!in the title)Checklist:
make hookschecks it on commit).docs/accordingly.## UnreleasedinCHANGELOG.md.Tests: full backend suite in memory (549 passed) and against a SurrealDB 3.2.4 server with surrealkv (549 passed); the one failure in both is the known LibreOffice conversion test. No API changes, so
openapi.jsonis unchanged.🤖 Generated with Claude Code
https://claude.ai/code/session_01CMvFKqsrnJrxxvME87LRk7