Skip to content

KVStore::remove Implementation Part 2#4

Open
vidyasilai wants to merge 54 commits intomathworks:mainfrom
vidyasilai:remove_impl
Open

KVStore::remove Implementation Part 2#4
vidyasilai wants to merge 54 commits intomathworks:mainfrom
vidyasilai:remove_impl

Conversation

@vidyasilai
Copy link
Copy Markdown
Collaborator

@vidyasilai vidyasilai commented Nov 12, 2025

This PR contains the tree algorithm code related to deletions:

  • Node and leaf merging
  • New InMemoryNode update buffer level type HybridLevel

@vidyasilai vidyasilai self-assigned this Nov 12, 2025
@vidyasilai vidyasilai marked this pull request as ready for review November 18, 2025 15:27

// If the root is a leaf and there are no items in the leaf, set the root to be an empty subtree.
//
if (batt::is_case<std::unique_ptr<InMemoryLeaf>>(this->impl_)) {
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.

Looks right, but maybe it should be in try_shrink instead..?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

//
Status Subtree::try_merge(BatchUpdateContext& context, Subtree&& sibling) noexcept
{
BATT_CHECK(!this->locked_.load());
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.

Maybe assert/check that this height and sibling height are equal?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

//
i32 sibling_i = pivot_i;

if (pivot_i == 0) {
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.

Should just be 2 cases.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

[&](auto& in_memory) -> Status {
using PtrT = std::decay_t<decltype(in_memory)>;

BATT_CHECK(batt::is_case<PtrT>(sibling.impl_));
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.

Would be good to add a message that interprets what this panic means.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done


BATT_CHECK(batt::is_case<PtrT>(sibling.impl_));
auto& sibling_ptr = std::get<PtrT>(sibling.impl_);
BATT_CHECK(sibling_ptr);
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.

nit: BATT_CHECK_NOT_NULLPTR

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

update_context.page_loader,
update_context.overcommit)
.merge_pivots(left_pivot_i, right_pivot_i);
}
Copy link
Copy Markdown
Collaborator

@tonyastolfi tonyastolfi Mar 31, 2026

Choose a reason for hiding this comment

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

Maybe comment why we don't care about the other cases?

Should probably be a batt::case_of

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

this->pending_bytes[left_pivot_i] += this->pending_bytes[right_pivot_i];
this->pending_bytes.erase(this->pending_bytes.begin() + right_pivot_i);

bool is_pending_bytes_exact = get_bit(this->pending_bytes_is_exact, left_pivot_i) &
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.

& -> &&

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

BATT_CHECK(sibling->result_set);

if (sibling->result_set->empty()) {
BATT_CHECK(batt::is_case<Viable>(this->get_viability()));
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.

Message would be helpful here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done

this->edit_size_totals = std::move(sibling->edit_size_totals);
return OkStatus();
}

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.

We should probably retain a pin on the sibling's leaf page in this; that means we may need to change InMemoryLeaf::pinned_leaf_page_ into pinned_leaf_pages_.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

In regard to turning pinned_leaf_page_ into a vector of pinned pages, what are your thoughts on keeping pinned_leaf_page_ as is and adding another member variable to InMemoryLeaf that is of type std::vector<PinnedPage> that holds pins on sibling pages as a result of merges? My reason for suggesting this is due to the logic in InMemoryLeaf::apply_batch_update, where we check the state of pinned_leaf_page_ to determine how to extract result_set.

if (flush_status == batt::StatusCode::kUnavailable) {
BATT_REQUIRE_OK(this->try_shrink());
}

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.

After a successful flush, we should do something here to see if the root object has changed; if it has, then we should reset the retries counter, since limiting to kMaxPivots is based on the assumption of a single node.

return false;
},
[](const NeedsMerge& needs_merge) {
return !needs_merge.single_pivot;
Copy link
Copy Markdown
Collaborator

@tonyastolfi tonyastolfi Apr 7, 2026

Choose a reason for hiding this comment

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

&& !needs_merge.no_items

@@ -441,6 +441,9 @@ inline usize PackedLeafLayoutPlan::compute_trie_step_size() const
step_size = 1;
} else if (this->key_count < 256) {
step_size = this->key_count / 16;
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.

Consider rounding up instead..?


/** \brief Removes the specified number (`count`) pivots from the end of the bit set.
*/
BATT_ALWAYS_INLINE void pop_back_pivots(i32 count)
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.

maybe push_front_pivots?


// Erase potential duplicate segments that have resulted from a previous split.
//
this->segments.erase(std::unique(this->segments.begin(),
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.

We do need to do this de-duplication step, but we need to calculate:

  1. union of the active pivots set
  2. "joining" of the portions of the piecewise-filter that correspond to each sibling's "side."

{
bool left_is_active = this->segment_.is_pivot_active(left_pivot);
bool right_is_active = this->segment_.is_pivot_active(right_pivot);
this->segment_.set_pivot_active(left_pivot, left_is_active | right_is_active);
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.

| -> ||

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.

2 participants