Files
turso/core
Preston Thorpe 306bc7e264 Merge 'Improve WAL checkpointing performance' from Preston Thorpe
###  General idea:
(outside of other optimizations made mostly around concurrency):
**When checkpointing, use pages from the PageCache if we can determine
that they are exactly the page/frame that we want.**
e.g. if the frame_cache has an entry:
`Page ID: 104 -> Frame ID's: [1001, 1002]`
and the OngoingCheckpoint has min_frame of 999 and max_frame of 1020, we
should be able to check the PageCache and see if it has page 104, and
only if it is tagged with frame_id = 1002, can we use that page to
backfill the DB file.
Since using a cached page during checkpoint is purely an optimization,
we can be conservative in terms of when we accept that a cached page is
valid to use. I came up with a `wal_tag` which is the frame_id +
checkpoint_seq, which is set only in the two following places:
1. When explicitly reading a frame from the WAL. (inside
Wall::read_frame)
 - read_frame is perhaps the most obvious path of ensuring it's the
exact page + frame combination that we want.
2. When appending a frame to the log during the normal process of
writing (during `[Pager::cacheflush]`)
 - cacheflush calls append_frame, and inside the Completion, the dirty
flag is cleared, and the wal_tag flag is set to the frame_id.
Inside `finish_read_page` (which is called for every page we read from
either the DB file or WAL.. the `wal_tag` is cleared along with the
`dirty` flag, so that any re-used `PageRef's` don't contain wal_tag's
from any previous or stale pages.
#### **Proposal**:
(In order to merge and simultaneously be able to sleep at night)
there is this debug assertion:
```rust
  #[cfg(debug_assertions)]
      {
           let mut raw = vec![0u8; self.page_size() as usize + WAL_FRAME_HEADER_SIZE];
           self.io.wait_for_completion(self.read_frame_raw(target_frame, &mut raw)?)?;
           let (_, wal_page) = sqlite3_ondisk::parse_wal_frame_header(&raw);
           let cached = cached_page.get_contents().buffer.as_slice();
          // while being horrible for performance, we can ensure that the bytes are identical 
          // when using the cached page vs what we would otherwise have read from disk.
           turso_assert!(wal_page == cached, "cache fast-path returned wrong content for page {page_id} frame {target_frame}");
      }
```
Performance
=====================================
Average latency for a checkpoint on my local machine:
#### Before: `7-12ms`
#### After: `2-5ms`

Reviewed-by: Nikita Sivukhin (@sivukhin)

Closes #2568
2025-08-20 18:57:14 -04:00
..
2025-08-15 17:08:53 -04:00
2025-06-23 19:52:13 +01:00
2025-06-30 10:01:03 +03:00
2025-08-15 17:08:53 -04:00
2025-01-28 14:55:38 -05:00
2025-08-19 10:48:21 -03:00
2025-06-23 19:52:13 +01:00
2025-08-12 16:42:38 +05:30
2025-01-28 14:55:38 -05:00
2025-06-23 19:52:13 +01:00
2025-06-30 09:54:13 +03:00