From e88707c6fd55c567b9b272f9e7547a751e6c0004 Mon Sep 17 00:00:00 2001 From: Jussi Saurio Date: Thu, 31 Jul 2025 13:51:39 +0300 Subject: [PATCH] fix/wal: only rollback WAL if txn was write --- core/lib.rs | 2 +- core/storage/pager.rs | 21 ++++++++++++++++----- core/storage/wal.rs | 3 ++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/core/lib.rs b/core/lib.rs index 6f26ea0d0..318ca4f78 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -1190,7 +1190,7 @@ impl Connection { wal.end_read_tx(); } // remove all non-commited changes in case if WAL session left some suffix without commit frame - pager.rollback(false, self)?; + pager.rollback(false, self, true)?; } // let's re-parse schema from scratch if schema cookie changed compared to the our in-memory view of schema diff --git a/core/storage/pager.rs b/core/storage/pager.rs index 97979b43e..f3b6be3d0 100644 --- a/core/storage/pager.rs +++ b/core/storage/pager.rs @@ -815,14 +815,15 @@ impl Pager { ) -> Result> { tracing::trace!("end_tx(rollback={})", rollback); if rollback { - if matches!( + let is_write = matches!( connection.transaction_state.get(), TransactionState::Write { .. } - ) { + ); + if is_write { self.wal.borrow().end_write_tx(); } self.wal.borrow().end_read_tx(); - self.rollback(schema_did_change, connection)?; + self.rollback(schema_did_change, connection, is_write)?; return Ok(IOResult::Done(PagerCommitResult::Rollback)); } let commit_status = self.commit_dirty_pages(wal_checkpoint_disabled)?; @@ -1799,9 +1800,17 @@ impl Pager { &self, schema_did_change: bool, connection: &Connection, + is_write: bool, ) -> Result<(), LimboError> { tracing::debug!(schema_did_change); - self.dirty_pages.borrow_mut().clear(); + if is_write { + self.dirty_pages.borrow_mut().clear(); + } else { + turso_assert!( + self.dirty_pages.borrow().is_empty(), + "dirty pages should be empty for read txn" + ); + } let mut cache = self.page_cache.write(); self.reset_internal_states(); @@ -1811,7 +1820,9 @@ impl Pager { if schema_did_change { connection.schema.replace(connection._db.clone_schema()?); } - self.wal.borrow_mut().rollback()?; + if is_write { + self.wal.borrow_mut().rollback()?; + } Ok(()) } diff --git a/core/storage/wal.rs b/core/storage/wal.rs index f3b67f08b..3ef4df3b8 100644 --- a/core/storage/wal.rs +++ b/core/storage/wal.rs @@ -1292,6 +1292,7 @@ impl WalFile { let header = unsafe { shared.get().as_mut().unwrap().wal_header.lock() }; let last_checksum = unsafe { (*shared.get()).last_checksum }; + let start_pages_in_frames = unsafe { (*shared.get()).pages_in_frames.lock().len() }; Self { io, // default to max frame in WAL, so that when we read schema we can read from WAL too if it's there. @@ -1315,7 +1316,7 @@ impl WalFile { last_checksum, prev_checkpoint: CheckpointResult::default(), checkpoint_guard: None, - start_pages_in_frames: 0, + start_pages_in_frames, header: *header, } }