Skip to content

fix: continue replicating after restoring a database - #25

Merged
mcollina merged 1 commit into
mainfrom
fix/restore-position
Oct 29, 2026
Merged

mcollina merged 1 commit into
mainfrom
fix/restore-position

Conversation

@mcollina

@mcollina mcollina commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Fixes #24.

Restoring a database into the replica's own path, as ensureExists() does on a new host, left the local position at zero. The next sync() wrote TXID 1, which the replica already had, so the new changes were never uploaded and a later restore silently lost them.

Changes

  • Restore resets the local state. When the output path is the replica's own database, restore discards the local LTX cache and records the restored TXID in <meta>/base-txid. pos() takes it into account, and the next sync() continues with the following TXID, starting from a snapshot.
  • Stale WAL files are removed. Restore deletes the -wal and -shm files at the output path before moving the restored database into place, so SQLite doesn't replay them on top of it.
  • sync() no longer skips uploads silently. It throws the new ErrReplicaAhead (ERR_REPLICA_AHEAD, with localTXID and remoteTXID) when the replica is ahead of the local database.

Tests

Three integration tests in test/integration/replicate-restore.test.ts:

  • writes after ensureExists() reach the replica, and a third restore sees them;
  • a restore over a database with a stale WAL returns the restored data;
  • sync() throws ErrReplicaAhead when the replica is ahead.

All tests pass, including the Postgres and S3 backends, and typecheck and lint are clean. README and CHANGELOG are updated.

🤖 Generated with Claude Code

Restoring a database into the replica's own path, as ensureExists() does
on a new host, left the local position at zero. The next sync() wrote
TXID 1, which the replica already had, so the new changes were never
uploaded and a later restore silently lost them.

Restore now discards the local LTX cache and records the restored TXID,
so the next sync() continues with the following TXID from a snapshot. It
also removes the -wal and -shm files left at the output path, which
SQLite would replay on top of the restored database. sync() throws
ErrReplicaAhead instead of skipping uploads when the replica is ahead of
the local database.

Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollina
mcollina merged commit d6978ff into main Oct 29, 2026
3 checks passed
@mcollina
mcollina deleted the fix/restore-position branch October 29, 2026 09:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Writes after a restore are never uploaded

1 participant