Skip to content

Commit 8cc4748

Browse files
authored
Merge pull request #6 from vidyasilai/remove_impl_part1
KVStore::remove Implementation Part 1
2 parents c31b151 + 15744a4 commit 8cc4748

17 files changed

+504
-160
lines changed

src/turtle_kv/core/algo/compute_running_total.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ namespace turtle_kv {
1414

1515
//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
1616
//
17-
template <bool kDecayValue = false>
17+
template <bool kDecayValue>
1818
inline batt::RunningTotal compute_running_total(
1919
batt::WorkerPool& worker_pool,
20-
const MergeCompactor::ResultSet</*decay_to_items=*/false>& result_set,
20+
const MergeCompactor::ResultSet<kDecayValue>& result_set,
2121
DecayToItem<kDecayValue> decay_to_item [[maybe_unused]] = {})
2222
{
2323
auto merged_edits = result_set.get();

src/turtle_kv/core/merge_compactor.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,12 @@ template <bool kDecayToItems>
429429
/*static*/ auto MergeCompactor::ResultSet<kDecayToItems>::concat(ResultSet&& first,
430430
ResultSet&& second) -> ResultSet
431431
{
432+
if (first.size() > 0 && second.size() > 0) {
433+
BATT_CHECK_LT(first.get_max_key(), second.get_min_key())
434+
<< "All elements in the first ResultSet should be strictly less than the elements in the "
435+
"second ResultSet!";
436+
}
437+
432438
ResultSet ans;
433439

434440
//----- --- -- - - - -
@@ -495,6 +501,8 @@ template <bool kDecayToItems>
495501
chunk_from_second.offset += first_size;
496502
});
497503

504+
ans.chunks_.back().offset = first_size + second.chunks_.back().offset;
505+
498506
first.clear();
499507
second.clear();
500508

@@ -1147,4 +1155,4 @@ u64 MergeCompactor::ResultSet<kDecayToItems>::get_packed_size() const
11471155
template class MergeCompactor::ResultSet</*kDecayToItems=*/false>;
11481156
template class MergeCompactor::ResultSet</*kDecayToItems=*/true>;
11491157

1150-
} // namespace turtle_kv
1158+
} // namespace turtle_kv

src/turtle_kv/core/merge_compactor.test.cpp

Lines changed: 185 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <gmock/gmock.h>
66
#include <gtest/gtest.h>
77

8+
#include <turtle_kv/core/testing/generate.hpp>
89
#include <turtle_kv/import/env.hpp>
910

1011
#include <batteries/stream_util.hpp>
@@ -23,7 +24,10 @@ using namespace batt::int_types;
2324
using batt::as_seq;
2425
using batt::WorkerPool;
2526

27+
using llfs::StableStringStore;
28+
2629
using turtle_kv::CInterval;
30+
using turtle_kv::DecayToItem;
2731
using turtle_kv::EditSlice;
2832
using turtle_kv::EditView;
2933
using turtle_kv::getenv_as;
@@ -39,6 +43,8 @@ using turtle_kv::Status;
3943
using turtle_kv::StatusOr;
4044
using turtle_kv::ValueView;
4145

46+
using turtle_kv::testing::RandomStringGenerator;
47+
4248
namespace seq = turtle_kv::seq;
4349

4450
constexpr usize kNumKeys = 16;
@@ -482,4 +488,182 @@ TEST(MergeCompactor, ResultSetDropKeyRange)
482488
}
483489
}
484490

485-
} // namespace
491+
//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
492+
//
493+
class ResultSetConcatTest : public ::testing::Test
494+
{
495+
public:
496+
void generate_edits(usize num_edits)
497+
{
498+
std::unordered_set<KeyView> keys_set;
499+
500+
std::default_random_engine rng{/*seed=*/30};
501+
RandomStringGenerator generate_key;
502+
while (this->all_edits_.size() < num_edits) {
503+
KeyView key = generate_key(rng, this->store_);
504+
if (keys_set.contains(key)) {
505+
continue;
506+
}
507+
keys_set.emplace(key);
508+
this->all_edits_.emplace_back(key,
509+
ValueView::from_str(this->store_.store(std::string(100, 'a'))));
510+
}
511+
512+
std::sort(this->all_edits_.begin(), this->all_edits_.end(), KeyOrder{});
513+
}
514+
515+
template <bool kDecayToItems>
516+
MergeCompactor::ResultSet<kDecayToItems> concat(std::vector<EditView>&& first,
517+
std::vector<EditView>&& second,
518+
DecayToItem<kDecayToItems> decay_to_item)
519+
{
520+
usize first_size = first.size();
521+
usize second_size = second.size();
522+
523+
MergeCompactor::ResultSet<kDecayToItems> first_result_set;
524+
first_result_set.append(std::move(first));
525+
MergeCompactor::ResultSet<kDecayToItems> second_result_set;
526+
second_result_set.append(std::move(second));
527+
528+
EXPECT_EQ(first_result_set.size(), first_size);
529+
EXPECT_EQ(second_result_set.size(), second_size);
530+
531+
MergeCompactor::ResultSet<kDecayToItems> concatenated_result_set =
532+
MergeCompactor::ResultSet<kDecayToItems>::concat(std::move(first_result_set),
533+
std::move(second_result_set));
534+
535+
return concatenated_result_set;
536+
}
537+
538+
template <bool kDecayToItems>
539+
void verify_result_set(const MergeCompactor::ResultSet<kDecayToItems>& result_set,
540+
const std::vector<EditView>& edits)
541+
{
542+
EXPECT_EQ(result_set.size(), edits.size());
543+
544+
usize i = 0;
545+
for (const EditView& edit : result_set.get()) {
546+
EXPECT_EQ(edit, edits[i]);
547+
++i;
548+
}
549+
}
550+
551+
llfs::StableStringStore store_;
552+
std::vector<EditView> all_edits_;
553+
};
554+
555+
//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
556+
//
557+
TEST_F(ResultSetConcatTest, Concat)
558+
{
559+
// Generate an edit batch of size 200.
560+
//
561+
usize n = 200;
562+
this->generate_edits(n);
563+
564+
// Divide the edit batch in half, and create ResultSet objects out of each half.
565+
//
566+
std::vector<EditView> first{this->all_edits_.begin(), this->all_edits_.begin() + (n / 2)};
567+
std::vector<EditView> second{this->all_edits_.begin() + (n / 2), this->all_edits_.end()};
568+
569+
MergeCompactor::ResultSet<false> concatenated_result_set =
570+
this->concat(std::move(first), std::move(second), DecayToItem<false>{});
571+
572+
// Concatenated ResultSet should have the same size as the original edit batch, and should
573+
// also contain the same items in the same order.
574+
//
575+
this->verify_result_set(concatenated_result_set, this->all_edits_);
576+
577+
// Now, repeat the process with unequal sized inputs.
578+
//
579+
first.assign(this->all_edits_.begin(), this->all_edits_.begin() + (n / 4));
580+
second.assign(this->all_edits_.begin() + (n / 4), this->all_edits_.end());
581+
582+
concatenated_result_set = this->concat(std::move(first), std::move(second), DecayToItem<false>{});
583+
584+
this->verify_result_set(concatenated_result_set, this->all_edits_);
585+
586+
// Finally, test with empty input.
587+
//
588+
first = {};
589+
second.assign(this->all_edits_.begin(), this->all_edits_.begin() + (n / 4));
590+
591+
concatenated_result_set = this->concat(std::move(first), std::move(second), DecayToItem<false>{});
592+
593+
this->verify_result_set(concatenated_result_set,
594+
{this->all_edits_.begin(), this->all_edits_.begin() + (n / 4)});
595+
596+
first.assign(this->all_edits_.begin(), this->all_edits_.begin() + (n / 4));
597+
second = {};
598+
599+
concatenated_result_set = this->concat(std::move(first), std::move(second), DecayToItem<false>{});
600+
601+
this->verify_result_set(concatenated_result_set,
602+
{this->all_edits_.begin(), this->all_edits_.begin() + (n / 4)});
603+
604+
first = {};
605+
second = {};
606+
concatenated_result_set = this->concat(std::move(first), std::move(second), DecayToItem<false>{});
607+
EXPECT_EQ(concatenated_result_set.size(), 0);
608+
}
609+
610+
//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
611+
//
612+
TEST_F(ResultSetConcatTest, FragmentedConcat)
613+
{
614+
usize n = 200;
615+
this->generate_edits(n);
616+
617+
std::vector<EditView> first{this->all_edits_.begin(), this->all_edits_.begin() + (n / 2)};
618+
std::vector<EditView> second{this->all_edits_.begin() + (n / 2), this->all_edits_.end()};
619+
620+
MergeCompactor::ResultSet<false> first_result_set;
621+
first_result_set.append(std::move(first));
622+
MergeCompactor::ResultSet<false> second_result_set;
623+
second_result_set.append(std::move(second));
624+
625+
// Drop some keys fron the beginning of the ResultSet.
626+
//
627+
first_result_set.drop_before_n(n / 10);
628+
629+
// Drop some keys in the middle of the ResultSet.
630+
//
631+
auto second_range_begin = this->all_edits_.begin() + (3 * n / 5);
632+
auto second_range_end = this->all_edits_.begin() + (3 * n / 4);
633+
Interval<KeyView> second_range{second_range_begin->key, second_range_end->key};
634+
second_result_set.drop_key_range_half_open(second_range);
635+
636+
MergeCompactor::ResultSet<false> concatenated_result_set =
637+
MergeCompactor::ResultSet<false>::concat(std::move(first_result_set),
638+
std::move(second_result_set));
639+
640+
std::vector<EditView> concat_edits{this->all_edits_.begin() + (n / 10),
641+
this->all_edits_.begin() + (3 * n / 5)};
642+
concat_edits.insert(concat_edits.end(),
643+
this->all_edits_.begin() + (3 * n / 4),
644+
this->all_edits_.end());
645+
this->verify_result_set(concatenated_result_set, concat_edits);
646+
}
647+
648+
//==#==========+==+=+=++=+++++++++++-+-+--+----- --- -- - - - -
649+
//
650+
TEST_F(ResultSetConcatTest, ConcatDeath)
651+
{
652+
usize n = 200;
653+
this->generate_edits(n);
654+
655+
std::vector<EditView> first{this->all_edits_.begin(), this->all_edits_.begin() + (n / 2)};
656+
std::vector<EditView> second{this->all_edits_.begin() + (n / 2), this->all_edits_.end()};
657+
658+
// Undo the sorting.
659+
//
660+
std::swap(first.back(), second.front());
661+
662+
// We should panic since first and second have overlapping key ranges.
663+
//
664+
EXPECT_DEATH(this->concat(std::move(first), std::move(second), DecayToItem<false>{}),
665+
"All elements in the first ResultSet should be strictly less than the elements in "
666+
"the second ResultSet!");
667+
}
668+
669+
} // namespace

src/turtle_kv/core/testing/generate.hpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <random>
1313
#include <string>
1414
#include <string_view>
15+
#include <unordered_set>
1516
#include <vector>
1617

1718
namespace turtle_kv {
@@ -184,21 +185,35 @@ class RandomResultSetGenerator : public MinMaxSize<usize{1} << 24>
184185
}
185186

186187
template <bool kDecayToItems, typename Rng>
187-
MergeCompactor::ResultSet</*kDecayToItems=*/kDecayToItems>
188-
operator()(DecayToItem<kDecayToItems>, Rng& rng, llfs::StableStringStore& store)
188+
MergeCompactor::ResultSet</*kDecayToItems=*/kDecayToItems> operator()(
189+
DecayToItem<kDecayToItems>,
190+
Rng& rng,
191+
llfs::StableStringStore& store,
192+
const std::vector<KeyView>& to_delete)
189193
{
190194
using ResultSet = MergeCompactor::ResultSet</*kDecayToItems=*/kDecayToItems>;
191195
using Item = typename ResultSet::value_type;
192196

193197
const usize n = this->Super::pick_size(rng);
194198
std::vector<EditView> items;
195199

200+
for (const KeyView& delete_key : to_delete) {
201+
items.emplace_back(delete_key, ValueView::deleted());
202+
}
203+
204+
std::unordered_set<KeyView> deleted_items_set{to_delete.begin(), to_delete.end()};
196205
while (items.size() < n) {
197-
for (usize i = items.size(); i < n; ++i) {
206+
for (usize i = items.size(); i < n;) {
198207
char ch = '_' + (i & 31);
199-
items.emplace_back(this->key_generator_(rng, store),
208+
KeyView key = this->key_generator_(rng, store);
209+
if (deleted_items_set.count(key)) {
210+
continue;
211+
}
212+
items.emplace_back(key,
200213
ValueView::from_str(store.store(std::string(this->value_size_, ch))));
214+
++i;
201215
}
216+
202217
std::sort(items.begin(), items.end(), KeyOrder{});
203218
items.erase(std::unique(items.begin(),
204219
items.end(),
@@ -221,4 +236,4 @@ class RandomResultSetGenerator : public MinMaxSize<usize{1} << 24>
221236
};
222237

223238
} // namespace testing
224-
} // namespace turtle_kv
239+
} // namespace turtle_kv

src/turtle_kv/core/testing/generate.test.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@
77

88
namespace {
99

10+
using batt::int_types::usize;
11+
1012
using turtle_kv::DecayToItem;
1113
using turtle_kv::ItemView;
1214
using turtle_kv::KeyOrder;
15+
using turtle_kv::KeyView;
16+
using turtle_kv::StatusOr;
17+
using turtle_kv::ValueView;
1318
using turtle_kv::testing::RandomResultSetGenerator;
1419

1520
template <bool kDecayToItems>
@@ -24,10 +29,28 @@ TEST(GenerateTest, Test)
2429

2530
g.set_size(200);
2631

27-
ResultSet<true> result_set = g(DecayToItem<true>{}, rng, store);
32+
std::vector<KeyView> to_delete;
33+
ResultSet<true> result_set = g(DecayToItem<true>{}, rng, store, to_delete);
2834

2935
EXPECT_TRUE(std::is_sorted(result_set.get().begin(), result_set.get().end(), KeyOrder{}));
3036
EXPECT_EQ(result_set.get().size(), 200u);
37+
38+
auto result_set_slice = result_set.get();
39+
usize i = 0;
40+
for (const ItemView& edit : result_set_slice) {
41+
if (i % 2) {
42+
to_delete.emplace_back(edit.key);
43+
}
44+
++i;
45+
}
46+
47+
ResultSet<false> result_set_with_deletes = g(DecayToItem<false>{}, rng, store, to_delete);
48+
for (const KeyView& deleted_key : to_delete) {
49+
StatusOr<ValueView> deleted_value = result_set_with_deletes.find_key(deleted_key);
50+
EXPECT_TRUE(deleted_value.ok());
51+
EXPECT_EQ(*deleted_value, ValueView::deleted());
52+
}
53+
EXPECT_EQ(to_delete.size(), result_set_with_deletes.size() / 2);
3154
}
3255

33-
} // namespace
56+
} // namespace

0 commit comments

Comments
 (0)