Skip to content

Upgrading from legacy SQLite shards

Startup recovers interrupted re-embed swaps, converts libSQL DiskANN indexes to the current Turso engine, and then verifies or upgrades the legacy shard schema. Engine conversion runs even when a store already has a completed legacy migration marker, and preserves stored vectors without re-embedding. Each converted database is backed up as <database>.pre-tursodb-<timestamp>.bak.

On macOS and Linux, multiple OpenCode sessions can share the same storagePath via Turso’s experimental multiprocess_wal (every process must use the same mode — restart all sessions after upgrading). On Windows the engine rejects that flag, so only one OpenCode session can own the memory databases at a time.

tursoMultiprocessWal: false opens in single-owner mode instead: other processes then fail with database is locked rather than sharing the store. That is the safer choice if non-plugin tools touch the same files, because a writing foreign SQLite process makes Turso’s WAL index stale and its own next write is silently lost. tursoAutoHeal (default true) moves a store’s -shm/-tshm index sidecars into storagePath/recovery/<db>-<timestamp>/ and retries once when an open fails with a stale WAL index, after verifying a copy of the main database. A non-empty -wal is never moved, so committed but uncheckpointed transactions survive; only an absent or 0-byte -wal is removed. Concurrent sessions serialise healing through storagePath/recovery/<db>.lock.

On first startup after upgrading, opencode-mem automatically migrates existing memory shard databases to native Turso/libSQL vector format:

  • Each shard is backed up as <shard>.db.legacy.bak before rewrite
  • Progress is tracked per shard in <shard>.db.turso-migrate.json
  • A global marker .turso-migrated is written only after all shards verify successfully
  • Normal inserts and deletes do not invalidate a completed shard migration; startup still checks that its vectors are readable and match its stored dimensions
  • Do not run multiple OpenCode instances against the same storagePath during migration; a lock file (.turso-migrate.lock) prevents concurrent migration
  • Manual dimension migrations use .turso-operation.lock; other plugin processes reject new memory writes until the migration finishes
  • Locks expire after 30 minutes without a progress timestamp update (or file modification time if the timestamp is unavailable), even if the owning process is alive. This lease uses wall-clock time: a forward clock step greater than 30 minutes can expire a progressing operation early; a backward step or future-dated lock can extend an inert lock’s life. Locks carry a unique acquisition id, and stale cleanup only removes the exact file it inspected. Migrations heartbeat as they progress and abort if they discover that ownership was lost. Stale-lock cleanup is logged for diagnosis

If migration is interrupted, the next startup resumes from the backup automatically.

If a shard becomes incompatible (for example after changing embeddingDimensions), writes are blocked and the original database is left untouched. Use the Web UI’s re-embed migration to build and verify a replacement before it is swapped into place. The previous shard remains available as <shard>.db.pre-reembed-<pid>-<timestamp>.bak.