Skip to content

Conversation

@vidyasilai
Copy link
Collaborator

Support for deleting a key/value pair from the KVStore.

@vidyasilai vidyasilai self-assigned this Nov 12, 2025
@vidyasilai vidyasilai marked this pull request as ready for review November 18, 2025 15:27
chunk_from_second.offset += first_size;
});

ans.chunks_.back().offset = first_size + second.chunks_.back().offset;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add regression test for this fix.

while (items.size() < n) {
for (usize i = items.size(); i < n; ++i) {
char ch = '_' + (i & 31);
items.emplace_back(this->key_generator_(rng, store),
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should probably check to make sure we don't generate one of the to_delete keys, since that would bring the key back to life.

*value = combine(*value, *delta_value);
if (!value->needs_combine()) {
this->metrics_.delta_log2_get_count[batt::log2_ceil(observed_deltas_size - i)].add(1);
if (value->is_delete()) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider abstracting to remove the duplicate pattern.

}
}

TEST(MergeCompactor, ResultSetConcat)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Good test; I have a few suggestions for improvement:

  1. A few comments, to structure the different phases of the test visually, would be nice
  2. Have test cases for boundary conditions, like one or the other input to concat being empty
  3. Verify failure for invalid inputs (like overlapping or swapped key ranges are rejected)
  4. It would be good to test that "fragmented" result sets are property concat'ed: so you could drop some key ranges:
    1. drop from first and or second
    2. drop from beginning, middle, or end
  5. Have a test where the two inputs are differently-sized

items.emplace_back(delete_key, ValueView::deleted());
}
if (items.size() > n) {
ResultSet result;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we maybe truncate items in this case? Or just error?

*
* If no merge, returns None.
*/
StatusOr<Optional<Subtree>> try_merge(BatchUpdateContext& context, Subtree& sibling) noexcept;
Copy link
Collaborator

Choose a reason for hiding this comment

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

After chatting, new design will be:

  1. sibling is always the right-sibling
  2. sibling passed as rvalue
  3. modify this in place instead of returning a copy on success
  4. unify the merge and borrow cases, by having try_merge return None if its a 2 -> 1 merge, otherwise have it return the new right-sibling if its a 2 -> 2 borrow-style merge; that way the convention is the same as try_split.

*/
StatusOr<Optional<Subtree>> try_merge(BatchUpdateContext& context, Subtree& sibling) noexcept;

/** \brief Attempts to make the Subtree viable by borrowing data from one of its siblings.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This can be removed from Subtree after the previous comment is addressed.

BATT_ASSIGN_OK_RESULT(std::unique_ptr<InMemoryLeaf> merged_leaf, //
leaf->try_merge(context, std::move(sibling_leaf_ptr)));

BATT_CHECK_EQ(merged_leaf, nullptr);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should be handled the same as lines 622-624 below; then we should have a single generic implementation to replace the two lambdas.

auto new_leaf = std::make_unique<InMemoryLeaf>(llfs::PinnedPage{}, tree_options);

new_leaf->result_set = update.result_set;
new_leaf->result_set = std::move(update.context.decay_batch_to_items(update.result_set));
Copy link
Collaborator

Choose a reason for hiding this comment

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

no std::move is needed here.

update.context.merge_compact_edits</*decay_to_items=*/true>( //
global_max_key(),
[&](MergeCompactor& compactor) -> Status {
compactor.push_level(update.result_set.live_edit_slices());
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are we missing a call to update_has_page_refs in this case?

// Case: {BatchUpdate} + {InMemoryLeaf} => InMemoryLeaf

BATT_CHECK_EQ(parent_height, 2);

Copy link
Collaborator

Choose a reason for hiding this comment

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

We should unify this case (most of it) with the Batch + PackedLeaf => InMemoryLeaf case above (generic impl)

return OkStatus();
},
[&](NeedsSplit needs_split) {
if (needs_split.too_many_segments && !needs_split.too_many_pivots &&
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would you please add a TODO, to revisit this once the latest VLDB changes are merged in?

},

[&](const std::unique_ptr<InMemoryLeaf>& leaf [[maybe_unused]]) -> Status {
return OkStatus();
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should check to see if there are literally no items in the leaf, and if so, replace with an empty tree.

this->impl_,

[&](const llfs::PageIdSlot& page_id_slot [[maybe_unused]]) -> Status {
return {batt::StatusCode::kUnimplemented};
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe return OkStatus if the page id is invalid?

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