Skip to content

Fix stale child pointer bug in merge_branch_with_only_child#182

Merged
BrianBland merged 3 commits into
base:mainfrom
cffls:fix_invalid_cell
Jan 14, 2026
Merged

Fix stale child pointer bug in merge_branch_with_only_child#182
BrianBland merged 3 commits into
base:mainfrom
cffls:fix_invalid_cell

Conversation

@cffls

@cffls cffls commented Jan 6, 2026

Copy link
Copy Markdown
Contributor

When merging a branch node with its only remaining child on a different page, the child node was read and cached early in merge_branch_with_only_child. If the child's page had < 200 free bytes, split_page was called which moved some grandchildren to new pages and updated the child node's pointers at index 0. However, the stale cached node was then written back, overwriting split_page's pointer updates.

Fix: Defer reading the child node until after split_page completes. The helper functions now receive only the prefix to prepend, and read the child node fresh before modifying and writing it back. This ensures any pointer updates from split_page are preserved.

@cb-heimdall

cb-heimdall commented Jan 6, 2026

Copy link
Copy Markdown
Collaborator

✅ Heimdall Review Status

Requirement Status More Info
Reviews 1/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@BrianBland BrianBland left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, just had a minor request for more consistent argument naming to improve readability.

Comment thread src/storage/engine.rs Outdated
fn merge_with_child_on_same_page(
&self,
slotted_page: &mut SlottedPageMut<'_>,
page_index: u8,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Recommend renaming this to parent_cell_index for consistency with the new child_cell_index

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment thread src/storage/engine.rs Outdated
Comment on lines 1121 to 1130
// Delete both nodes and insert the merged one
slotted_page.delete_value(child_cell_index)?;
slotted_page.delete_value(page_index)?;

let only_child_node_index = slotted_page.insert_value(&only_child_node)?;
let new_cell_index = slotted_page.insert_value(&child_node)?;

// If we are the root of the page, we must insert at index 0
if page_index == 0 {
assert_eq!(only_child_node_index, page_index);
assert_eq!(new_cell_index, page_index);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is more a comment about the existing implementation, and not a critique of this changeset:

Ideally we should not be assuming and asserting the implementation details of the slotted page, specifically on insertion. It would be preferable to delete the child and then replace the parent's slot with the new node. For example:

slotted_page.delete_value(child_cell_index)?;
slotted_page.set_value(page_index, &child_node)?;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It seems like this is addressed in this PR: https://github.com/base/triedb/pull/183/changes . To avoid conflicts, I guess it is better to leave this one as it is.

@cffls

cffls commented Jan 12, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @BrianBland for the feedbacks.

@BrianBland

Copy link
Copy Markdown
Collaborator

@cffls Could you rebase your change to resolve merge conflicts with #183?

cffls added 2 commits January 13, 2026 14:45
When merging a branch node with its only remaining child on a different page, the child node was read and cached early in `merge_branch_with_only_child`. If the child's page had < 200 free bytes, `split_page` was called which moved some grandchildren to new pages and updated the child node's pointers at index 0. However, the stale cached node was then written back, overwriting `split_page`'s pointer updates.

Fix: Defer reading the child node until after `split_page` completes. The helper functions now receive only the prefix to prepend, and read the child node fresh before modifying and writing it back. This ensures any pointer updates from `split_page` are preserved.
@cffls

cffls commented Jan 13, 2026

Copy link
Copy Markdown
Contributor Author

@BrianBland rebased and resolved the conflicts.

@BrianBland
BrianBland self-requested a review January 13, 2026 23:01

@BrianBland BrianBland left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It looks like this PR is currently failing some linter checks. Could you please address these issues?

@BrianBland
BrianBland merged commit 86a74c2 into base:main Jan 14, 2026
17 checks passed
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.

3 participants