Skip to content

Conversation

@jussisaurio
Copy link
Collaborator

@jussisaurio jussisaurio commented Dec 15, 2025

Beef

This PR is entirely motivated by corruption bugs caused by marking pages as dirty after they had already been modified, causing the subjournal to restore dirty content upon rollback. For examples of this see #4231 and #4232.

Major change 1: Return a PageWriteGuard from add_dirty() and &mut PageContent is only accessible through that wrapper

  1. Change add_dirty() to return PageWriteGuard<'a> instead of Result<()>
  2. Make get_contents_mut() only accessible through PageWriteGuard::contents_mut()
  3. Added get_contents_mut_for_test() with #[cfg(test)] so that tests are less annoying
  4. Make Page::get() private, and expose things like pin count and flags through separate methods.
  5. Poke mutable holes for emptying or replacing page contents entirely for internals to use (i.e. this doesn't require going through add_dirty() because it doesn't make sense in those contexts, e.g. inside page cache)

Major change 2: Make a LOT of the PageContent methods require a mutable reference

  • Remove yolo method PageContent::as_ptr() that gets a mutable reference to the underlying buffer from an immutable. &PageContent, and replace it with regular as_slice and as_mut_slice
  • Make things like write_rightmost_ptr() require &mut
  • Rename get_contents_mut_for_test() from the first commit -> get_contents_mut_unsafe_dont_use() - should be only used in tests or places where subjournaling is never required OR the caller has manually verified that the page is dirty

@jussisaurio jussisaurio changed the title wip/btree/pager: introduce safer API for modifying pages btree/pager: make it way more difficult to mutate non-dirty pages Dec 16, 2025
@jussisaurio jussisaurio marked this pull request as ready for review December 16, 2025 10:11
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 @pereman2

This PR is entirely motivated by corruption bugs caused by marking pages as dirty
after they had already been modified, causing the subjournal to restore dirty
content upon rollback. For examples of this see #4231 and #4232.

Changes:

1. Change add_dirty() to return PageWriteGuard<'a> instead of Result<()>
2. Make get_contents_mut() only accessible through PageWriteGuard::contents_mut()
3. Added get_contents_mut_for_test() with #[cfg(test)] so that tests are less annoying
4. Make Page::get() private, and expose things like pin count and flags through separate methods.
5. Poke mutable holes for emptying or replacing page contents entirely for internals to use (i.e. this doesn't require going through add_dirty() because it doesn't make sense in those contexts, e.g. inside page cache)
@jussisaurio jussisaurio force-pushed the safer-page-access-api branch from d5b4040 to 5d64197 Compare December 17, 2025 12:37
}

pub fn overwrite_content(page: &PageRef, dest_offset: usize, new_payload: &[u8]) -> Result<()> {
turso_assert!(page.is_loaded(), "page should be loaded");
Copy link
Collaborator

Choose a reason for hiding this comment

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

is this assertion not required anymore? I'm working on a test that triggers this error (#4168, still a draft).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I brought it back. Didn't notice claude removed it for some reason even though I reviewed this multiple times :]

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.

4 participants