Skip to content

Conversation

@psrvere
Copy link

@psrvere psrvere commented Dec 24, 2025

This PR addresses feedback from tursodatabase/agentfs#119 suggesting that snapshot functionality should be implemented in turso.git using direct file copying rather than SQL-based row by row copying. Comment

Implementation:

  • TRUNCATE checkpoints the WAL
  • Takes a read lock on WAL before database file copy
  • Add Connection::snapshot API that checkpoints while keeping the lock and copies the database file
  • Now CLI's .clone uses this snapshot API

Assisted by: Cursor + Claude

@psrvere psrvere marked this pull request as ready for review December 24, 2025 14:53
Copy link

@turso-bot turso-bot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review @jussisaurio

@psrvere psrvere force-pushed the snapshot-file-copy branch 2 times, most recently from faa0e3e to 510cb7b Compare December 24, 2025 15:00
@penberg penberg changed the title Feature: Add Connection::snapshot() for point-in-time database copy Add connection snapshot support for point-in-time database copy Dec 25, 2025
Copy link
Collaborator

@sivukhin sivukhin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some suggestion and concerns regarding current tsnapshot logic.
Also, it will be great to use new snapshotting logic in the .clone CLI command

res.release_guard();
// Release checkpoint guard if lock is not to be kept
if !keep_lock {
res.release_guard();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that checkpoint guarantee that guard always will be set.

There are cases (for example, empty WAL - so nothing to checkpoint) - where checkpoint will return without holding any lock.

So, this fix do not prevent race condition situation in all situations, IIUC.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you are right. I didn't want to touch the checkpoint function in the first place.

core/lib.rs Outdated
let pager = self.pager.load();
let result = (|| -> Result<()> {
// Checkpoint and keep the lock
let _res = pager.blocking_checkpoint_keep_lock(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we instead of patching checkpoint do snapshot like this:

  1. Execute truncate checkpoint
  2. Start read transaction (either explicitly with BEGIN or maybe by just issuing read_tx on WAL)
  3. Do the copy of DB file
  4. End read transaction

Copy link
Author

@psrvere psrvere Dec 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this solution. I will implement this.

Although it does leave a tiny race window between truncating file and taking read lock where a writer can write and snapshot is taken. Maybe we can live with this right now. I will leave a detailed comment just in case we see a bug later on.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tiny race window between truncating file and taking read lock where a writer can write and snapshot is taken

Ah, yeah. The problem here actually is not in writes specifically - but in a checkpoint that can happen in the middle.

Alternative suggestion from me is to start read transaction before checkpoint but issue FULL checkpoint instead of TRUNCATE. This will make it possible for checkpoint to succeed - but also we will hold WAL and prevent any further checkpoint from happening (even if writes will happen).
Also, we should be careful with deferred nature of transaction and maybe we need to execute something after BEGIN statement in order to properly initiate read transaction.

As we are writing database - its better to be safe than sorry :) So even tiny race window is bad actually.

Copy link
Collaborator

@sivukhin sivukhin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, @psrvere, do you plan to add support of snapshot method in sdk-kit/Rust SDK later?

@psrvere
Copy link
Author

psrvere commented Dec 26, 2025

Thanks for the feedback @sivukhin.
I will update the .clone CLI command to use new snapshot logic and update SDK too in this PR. Will ping again once PR is ready to review.

This addresses feedback from tursodatabase/agentfs#119 suggesting that snapshot functionality should be implemented in turso.git using direct fily copying rather than SQL-based row by row copying.
Comment: https:://github.com/tursodatabase/agentfs/pull/119#issuecomment-3681336678

- extract core logic from Pager::checkpoint function to a new Pager::checkpoing_internal function and add a flag to keep_lock during the Finalize phase
- create wrapper functions Pager::checkpoint, Pager::checkpoint_with_lock and Pager::block_checkpoint_keep_lock
- add Connection::snapshot API that checkpoints while keeping the lock and copies the database file

The lock is held to avoid a race condition after finishing checkpointing and before copying the file when concurrent writers can write to db.

Signed-off-by: Prateek Singh Rathore <[email protected]>
Signed-off-by: Prateek Singh Rathore <[email protected]>
The previous implementation attempted to hold checkpoint locks during file copy by adding a keep_lock param to the checkpoint function.
However, checkpoint can have multiple early exit paths like empty WAL where no lock is acquired. Also, it clutters the checkpoint function.

This implementation executes TRUNCATE checkpoint and then acquires read lock on WAL before copying database file. This ensures new data is written
to WAL and new Checkpoint can not be taken before this read lock is released.

Signed-off-by: Prateek Singh Rathore <[email protected]>
@psrvere psrvere changed the title Add connection snapshot support for point-in-time database copy Feature: Add Snapshot for point-in-time database copy Dec 26, 2025
@github-actions github-actions bot added the cli label Dec 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants