diff options
author | kungurtsev <kungasc@ydb.tech> | 2024-01-17 14:52:26 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-17 14:52:26 +0100 |
commit | 5fa4b2f5ec340934d41d70a0ebc24375f12e92d8 (patch) | |
tree | 2e3fda10a9575768550c338f57c5a022dcb28418 | |
parent | 74fd99e8a690c4917ebca6d8d1ef637745951a04 (diff) | |
download | ydb-5fa4b2f5ec340934d41d70a0ebc24375f12e92d8.tar.gz |
KIKIMR-19522 BTreeIndex Iter Remove single run optimization (#995)
-rw-r--r-- | ydb/core/tablet_flat/benchmark/b_part_index.cpp | 10 | ||||
-rw-r--r-- | ydb/core/tablet_flat/flat_part_iter_multi.h | 76 | ||||
-rw-r--r-- | ydb/core/tablet_flat/test/libs/table/test_part.h | 5 | ||||
-rw-r--r-- | ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp | 570 | ||||
-rw-r--r-- | ydb/core/tablet_flat/ut/ut_btree_index_nodes.cpp (renamed from ydb/core/tablet_flat/ut/ut_btree_index.cpp) | 372 | ||||
-rw-r--r-- | ydb/core/tablet_flat/ut/ya.make | 3 |
6 files changed, 609 insertions, 427 deletions
diff --git a/ydb/core/tablet_flat/benchmark/b_part_index.cpp b/ydb/core/tablet_flat/benchmark/b_part_index.cpp index c991a2d13f..0f55324cd0 100644 --- a/ydb/core/tablet_flat/benchmark/b_part_index.cpp +++ b/ydb/core/tablet_flat/benchmark/b_part_index.cpp @@ -76,6 +76,8 @@ namespace { UNIT_ASSERT_GE(part->Stat.Bytes, 100ull*1024*1024); UNIT_ASSERT_LE(part->Stat.Bytes, 100ull*1024*1024 + 10ull*1024*1024); + UNIT_ASSERT_VALUES_EQUAL(part->Slices->size(), 1); + GroupId = TGroupId(groups ? 1 : 0); } @@ -192,18 +194,19 @@ BENCHMARK_DEFINE_F(TPartIndexSeekFixture, SeekKey)(benchmark::State& state) { BENCHMARK_DEFINE_F(TPartIndexIteratorFixture, DoReads)(benchmark::State& state) { const bool reverse = state.range(3); - const ui32 items = state.range(4); + const ESeek seek = static_cast<ESeek>(state.range(4)); + const ui32 items = state.range(5); for (auto _ : state) { auto it = Mass->Saved.Any(Rnd); if (reverse) { - CheckerReverse->Seek(*it, ESeek::Lower); + CheckerReverse->Seek(*it, seek); for (ui32 i = 1; CheckerReverse->GetReady() == EReady::Data && i < items; i++) { CheckerReverse->Next(); } } else { - Checker->Seek(*it, ESeek::Lower); + Checker->Seek(*it, seek); for (ui32 i = 1; Checker->GetReady() == EReady::Data && i < items; i++) { Checker->Next(); } @@ -242,6 +245,7 @@ BENCHMARK_REGISTER_F(TPartIndexIteratorFixture, DoReads) /* groups: */ {0, 1}, /* history: */ {0, 1}, /* reverse: */ {0, 1}, + /* ESeek: */ {1, 2, 3}, /* items */ {1, 10, 100}}) ->Unit(benchmark::kMicrosecond); diff --git a/ydb/core/tablet_flat/flat_part_iter_multi.h b/ydb/core/tablet_flat/flat_part_iter_multi.h index e714912634..a058804a47 100644 --- a/ydb/core/tablet_flat/flat_part_iter_multi.h +++ b/ydb/core/tablet_flat/flat_part_iter_multi.h @@ -297,12 +297,12 @@ namespace NTable { return EReady::Data; } - EReady SeekToStart() noexcept + EReady SeekToSliceFirstRow() noexcept { return Seek(BeginRowId); } - EReady SeekToEnd() noexcept + EReady SeekToSliceLastRow() noexcept { return Seek(EndRowId - 1); } @@ -828,16 +828,16 @@ namespace NTable { return Main.Seek(rowId); } - EReady SeekToStart() noexcept + EReady SeekToSliceFirstRow() noexcept { ClearKey(); - return Main.SeekToStart(); + return Main.SeekToSliceFirstRow(); } - EReady SeekToEnd() noexcept + EReady SeekToSliceLastRow() noexcept { ClearKey(); - return Main.SeekToEnd(); + return Main.SeekToSliceLastRow(); } EReady Next() noexcept @@ -1387,18 +1387,7 @@ namespace NTable { EReady Seek(const TCells key, ESeek seek) noexcept { - if (Run.size() == 1) { - // Avoid overhead of extra key comparisons in a single slice - Current = Run.begin(); - - if (!CurrentIt) { - InitCurrent(); - } - - return CurrentIt->Seek(key, seek); - } - - bool seekToStart = false; + bool seekToSliceFirstRow = false; TRun::const_iterator pos; switch (seek) { @@ -1409,7 +1398,7 @@ namespace NTable { case ESeek::Lower: if (!key) { pos = Run.begin(); - seekToStart = true; + seekToSliceFirstRow = true; break; } @@ -1417,8 +1406,8 @@ namespace NTable { if (pos != Run.end() && TSlice::CompareSearchKeyFirstKey(key, pos->Slice, *KeyCellDefaults) <= 0) { - // Key is at the start of the slice - seekToStart = true; + // key <= FirstKey + seekToSliceFirstRow = true; } break; @@ -1432,8 +1421,8 @@ namespace NTable { if (pos != Run.end() && TSlice::CompareSearchKeyFirstKey(key, pos->Slice, *KeyCellDefaults) < 0) { - // Key is at the start of the slice - seekToStart = true; + // key < FirstKey + seekToSliceFirstRow = true; } break; @@ -1452,7 +1441,7 @@ namespace NTable { UpdateCurrent(); } - if (!seekToStart) { + if (!seekToSliceFirstRow) { auto ready = CurrentIt->Seek(key, seek); if (ready != EReady::Gone) { return ready; @@ -1472,23 +1461,12 @@ namespace NTable { UpdateCurrent(); } - return SeekToStart(); + return SeekToSliceFirstRow(); } EReady SeekReverse(const TCells key, ESeek seek) noexcept { - if (Run.size() == 1) { - // Avoid overhead of extra key comparisons in a single slice - Current = Run.begin(); - - if (!CurrentIt) { - InitCurrent(); - } - - return CurrentIt->SeekReverse(key, seek); - } - - bool seekToEnd = false; + bool seekToSliceLastRow = false; TRun::const_iterator pos; switch (seek) { @@ -1498,7 +1476,7 @@ namespace NTable { case ESeek::Lower: if (!key) { - seekToEnd = true; + seekToSliceLastRow = true; pos = Run.end(); --pos; break; @@ -1508,7 +1486,8 @@ namespace NTable { if (pos != Run.end() && TSlice::CompareLastKeySearchKey(pos->Slice, key, *KeyCellDefaults) <= 0) { - seekToEnd = true; + // LastKey <= key + seekToSliceLastRow = true; } break; @@ -1522,7 +1501,8 @@ namespace NTable { if (pos != Run.end() && TSlice::CompareLastKeySearchKey(pos->Slice, key, *KeyCellDefaults) < 0) { - seekToEnd = true; + // LastKey < key + seekToSliceLastRow = true; } break; @@ -1541,7 +1521,7 @@ namespace NTable { UpdateCurrent(); } - if (!seekToEnd) { + if (!seekToSliceLastRow) { auto ready = CurrentIt->SeekReverse(key, seek); if (ready != EReady::Gone) { return ready; @@ -1563,7 +1543,7 @@ namespace NTable { UpdateCurrent(); } - return SeekToEnd(); + return SeekToSliceLastRow(); } EReady Next() noexcept @@ -1586,7 +1566,7 @@ namespace NTable { UpdateCurrent(); - ready = SeekToStart(); + ready = SeekToSliceFirstRow(); if (ready == EReady::Page) { // we haven't sought start, will do it again later Current--; @@ -1618,7 +1598,7 @@ namespace NTable { --Current; UpdateCurrent(); - ready = SeekToEnd(); + ready = SeekToSliceLastRow(); if (ready == EReady::Page) { // we haven't sought end, will do it again later Current++; @@ -1747,17 +1727,17 @@ namespace NTable { InitCurrent(); } - Y_FORCE_INLINE EReady SeekToStart() noexcept + Y_FORCE_INLINE EReady SeekToSliceFirstRow() noexcept { - auto ready = CurrentIt->SeekToStart(); + auto ready = CurrentIt->SeekToSliceFirstRow(); Y_ABORT_UNLESS(ready != EReady::Gone, "Unexpected slice without the first row"); return ready; } - Y_FORCE_INLINE EReady SeekToEnd() noexcept + Y_FORCE_INLINE EReady SeekToSliceLastRow() noexcept { - auto ready = CurrentIt->SeekToEnd(); + auto ready = CurrentIt->SeekToSliceLastRow(); Y_ABORT_UNLESS(ready != EReady::Gone, "Unexpected slice without the last row"); return ready; diff --git a/ydb/core/tablet_flat/test/libs/table/test_part.h b/ydb/core/tablet_flat/test/libs/table/test_part.h index f9f8458f6b..f67cb654ca 100644 --- a/ydb/core/tablet_flat/test/libs/table/test_part.h +++ b/ydb/core/tablet_flat/test/libs/table/test_part.h @@ -176,16 +176,15 @@ namespace NTest { return index.GetLastRecord(); } - inline const TPartIndexIt::TRecord * GetRecord(const TPartStore& part, TPageId pageId) { + inline const TPartIndexIt::TRecord * GetRecord(const TPartStore& part, TPageId pageIndex) { TTestEnv env; TPartIndexIt index(&part, &env, { }); Y_ABORT_UNLESS(index.Seek(0) == EReady::Data); - for (TPageId p = 0; p < pageId; p++) { + for (TPageId p = 0; p < pageIndex; p++) { Y_ABORT_UNLESS(index.Next() == EReady::Data); } - Y_ABORT_UNLESS(index.GetPageId() == pageId); return index.GetRecord(); } } diff --git a/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp b/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp new file mode 100644 index 0000000000..3c5acdb18e --- /dev/null +++ b/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp @@ -0,0 +1,570 @@ +#include "flat_page_btree_index.h" +#include "flat_part_btree_index_iter.h" +#include "flat_part_charge.h" +#include "flat_part_charge_btree_index.h" +#include "flat_part_iter_multi.h" +#include "test/libs/table/test_writer.h" +#include <ydb/core/tablet_flat/test/libs/rows/layout.h> +#include <library/cpp/testing/unittest/registar.h> + +namespace NKikimr::NTable::NPage { + +namespace { + using namespace NTest; + using TShortChild = TBtreeIndexNode::TShortChild; + using TChild = TBtreeIndexNode::TChild; + + struct TTouchEnv : public NTest::TTestEnv { + const TSharedData* TryGetPage(const TPart *part, TPageId pageId, TGroupId groupId) override + { + Touched[groupId].insert(pageId); + if (Loaded[groupId].contains(pageId)) { + return NTest::TTestEnv::TryGetPage(part, pageId, groupId); + } + return nullptr; + } + + void LoadTouched(bool clearLoaded) { + if (clearLoaded) { + Loaded.clear(); + } + for (const auto &g : Touched) { + Loaded[g.first].insert(g.second.begin(), g.second.end()); + } + Touched.clear(); + } + + TMap<TGroupId, TSet<TPageId>> Loaded; + TMap<TGroupId, TSet<TPageId>> Touched; + }; + + void AssertLoadTheSame(const TPartStore& part, const TTouchEnv& bTree, const TTouchEnv& flat, const TString& message) { + TSet<TGroupId> groupIds; + for (const auto &c : {bTree.Loaded, flat.Loaded}) { + for (const auto &g : c) { + groupIds.insert(g.first); + } + } + + for (TGroupId groupId : groupIds) { + TSet<TPageId> bTreeDataPages, flatDataPages; + for (TPageId pageId : bTree.Loaded.Value(groupId, TSet<TPageId>{})) { + if (part.GetPageType(pageId, groupId) == EPage::DataPage) { + bTreeDataPages.insert(pageId); + } + } + for (TPageId pageId : flat.Loaded.Value(groupId, TSet<TPageId>{})) { + if (part.GetPageType(pageId, groupId) == EPage::DataPage) { + flatDataPages.insert(pageId); + } + } + + UNIT_ASSERT_VALUES_EQUAL_C(flatDataPages, bTreeDataPages, message); + } + } + + TPartEggs MakePart(bool slices, ui32 levels) { + NPage::TConf conf; + switch (levels) { + case 0: + break; + case 1: + conf.Group(0).PageRows = 2; + break; + case 3: + conf.Group(0).PageRows = 2; + conf.Group(0).BTreeIndexNodeKeysMin = conf.Group(0).BTreeIndexNodeKeysMax = 2; + break; + default: + Y_Fail("Unknown levels"); + } + + TLayoutCook lay; + + lay + .Col(0, 0, NScheme::NTypeIds::Uint32) + .Col(0, 1, NScheme::NTypeIds::Uint32) + .Key({0, 1}); + + conf.WriteBTreeIndex = true; + + TPartCook cook(lay, conf); + + // making part with key gaps + const TVector<ui32> secondCells = {1, 3, 4, 6, 7, 8, 10}; + for (ui32 i : xrange(0u, 40u)) { + cook.Add(*TSchemedCookRow(*lay).Col(i / 7, secondCells[i % 7])); + } + + TPartEggs eggs = cook.Finish(); + + const auto part = *eggs.Lone(); + + if (slices) { + TSlices slices; + auto partSlices = (TSlices*)part.Slices.Get(); + + auto getKey = [&] (const NPage::TIndex::TRecord* record) { + TSmallVec<TCell> key; + for (const auto& info : part.Scheme->Groups[0].ColsKeyIdx) { + key.push_back(record->Cell(info)); + } + return TSerializedCellVec(key); + }; + auto add = [&](TRowId pageIndex1 /*inclusive*/, TRowId pageIndex2 /*exclusive*/) { + TSlice slice; + slice.FirstInclusive = true; + slice.FirstRowId = pageIndex1 * 2; + slice.FirstKey = pageIndex1 > 0 ? getKey(IndexTools::GetRecord(part, pageIndex1)) : partSlices->begin()->FirstKey; + slice.LastInclusive = false; + slice.LastRowId = pageIndex2 * 2; + slice.LastKey = pageIndex2 < 20 ? getKey(IndexTools::GetRecord(part, pageIndex2)) : partSlices->begin()->LastKey; + slices.push_back(slice); + }; + add(0, 2); + add(3, 4); + add(4, 6); + add(7, 8); + add(8, 9); + add(10, 14); + add(16, 17); + add(17, 19); + add(19, 20); + + partSlices->clear(); + for (auto s : slices) { + partSlices->push_back(s); + } + } + + if (slices) { + UNIT_ASSERT_GT(part.Slices->size(), 1); + } else { + UNIT_ASSERT_VALUES_EQUAL(part.Slices->size(), 1); + } + Cerr << "Slices" << Endl; + for (const auto &slice : *part.Slices) { + Cerr << " | "; + slice.Describe(Cerr); + Cerr << Endl; + } + Cerr << DumpPart(part, 3) << Endl; + + UNIT_ASSERT_VALUES_EQUAL(part.IndexPages.BTreeGroups[0].LevelCount, levels); + + return eggs; + } + + TVector<TCell> MakeKey(ui32 firstCell, ui32 secondCell) { + if (secondCell <= 11) { + // valid second cell [0 .. 11] + return {TCell::Make(firstCell), TCell::Make(secondCell)}; + } + if (secondCell == 12) { + return {TCell::Make(firstCell)}; + } + if (secondCell == 13) { + return { }; + } + Y_UNREACHABLE(); + } + + EReady Retry(std::function<EReady()> action, TTouchEnv& env, const TString& message, ui32 failsAllowed = 10) { + while (true) { + if (auto ready = action(); ready != EReady::Page) { + return ready; + } + env.LoadTouched(false); + UNIT_ASSERT_C(failsAllowed--, "Too many fails " + message); + } + Y_UNREACHABLE(); + } +} + +Y_UNIT_TEST_SUITE(TPartBtreeIndexIt) { + void AssertEqual(const TPartBtreeIndexIt& bTree, EReady bTreeReady, const TPartIndexIt& flat, EReady flatReady, const TString& message, bool allowFirstLastPageDifference = false) { + // Note: it's possible that B-Tree index don't return Gone status for keys before the first page or keys after the last page + if (allowFirstLastPageDifference && flatReady == EReady::Gone && bTreeReady == EReady::Data && + (bTree.GetRowId() == 0 || bTree.GetNextRowId() == bTree.GetEndRowId())) { + UNIT_ASSERT_C(bTree.IsValid(), message); + return; + } + + UNIT_ASSERT_VALUES_EQUAL_C(bTreeReady, flatReady, message); + UNIT_ASSERT_VALUES_EQUAL_C(bTree.IsValid(), flat.IsValid(), message); + if (flat.IsValid()) { + UNIT_ASSERT_VALUES_EQUAL_C(bTree.GetPageId(), flat.GetPageId(), message); + UNIT_ASSERT_VALUES_EQUAL_C(bTree.GetRowId(), flat.GetRowId(), message); + UNIT_ASSERT_VALUES_EQUAL_C(bTree.GetNextRowId(), flat.GetNextRowId(), message); + } + } + + EReady SeekRowId(IIndexIter& iter, TTouchEnv& env, TRowId rowId, const TString& message, ui32 failsAllowed = 10) { + return Retry([&]() { + return iter.Seek(rowId); + }, env, message, failsAllowed); + } + + EReady SeekLast(IIndexIter& iter, TTouchEnv& env, const TString& message, ui32 failsAllowed = 10) { + return Retry([&]() { + return iter.SeekLast(); + }, env, message, failsAllowed); + } + + EReady SeekKey(IIndexIter& iter, TTouchEnv& env, ESeek seek, bool reverse, TCells key, const TKeyCellDefaults *keyDefaults, const TString& message, ui32 failsAllowed = 10) { + return Retry([&]() { + if (reverse) { + return iter.SeekReverse(seek, key, keyDefaults); + } else { + return iter.Seek(seek, key, keyDefaults); + } + }, env, message, failsAllowed); + } + + EReady NextPrev(IIndexIter& iter, TTouchEnv& env, bool next, const TString& message, ui32 failsAllowed = 10) { + return Retry([&]() { + if (next) { + return iter.Next(); + } else { + return iter.Prev(); + } + }, env, message, failsAllowed); + } + + void CheckSeekRowId(const TPartStore& part) { + for (TRowId rowId1 : xrange(part.Stat.Rows + 1)) { + for (TRowId rowId2 : xrange(part.Stat.Rows + 1)) { + TTouchEnv bTreeEnv, flatEnv; + TPartBtreeIndexIt bTree(&part, &bTreeEnv, { }); + TPartIndexIt flat(&part, &flatEnv, { }); + + // checking initial seek: + { + TString message = TStringBuilder() << "SeekRowId< " << rowId1; + EReady bTreeReady = SeekRowId(bTree, bTreeEnv, rowId1, message); + EReady flatReady = SeekRowId(flat, flatEnv, rowId1, message); + UNIT_ASSERT_VALUES_EQUAL(bTreeReady, rowId1 < part.Stat.Rows ? EReady::Data : EReady::Gone); + AssertEqual(bTree, bTreeReady, flat, flatReady, message); + } + + // checking repositioning: + { + TString message = TStringBuilder() << "SeekRowId " << rowId1 << " -> " << rowId2; + EReady bTreeReady = SeekRowId(bTree, bTreeEnv, rowId2, message); + EReady flatReady = SeekRowId(flat, flatEnv, rowId2, message); + UNIT_ASSERT_VALUES_EQUAL(bTreeReady, rowId2 < part.Stat.Rows ? EReady::Data : EReady::Gone); + AssertEqual(bTree, bTreeReady, flat, flatReady, message); + } + } + } + } + + void CheckSeekLast(const TPartStore& part) { + TTouchEnv bTreeEnv, flatEnv; + TPartBtreeIndexIt bTree(&part, &bTreeEnv, { }); + TPartIndexIt flat(&part, &flatEnv, { }); + + TString message = TStringBuilder() << "SeekLast"; + EReady bTreeReady = SeekLast(bTree, bTreeEnv, message); + EReady flatReady = SeekLast(flat, flatEnv, message); + UNIT_ASSERT_VALUES_EQUAL(bTreeReady, EReady::Data); + AssertEqual(bTree, bTreeReady, flat, flatReady, message); + } + + void CheckSeekKey(const TPartStore& part, const TKeyCellDefaults *keyDefaults) { + for (bool reverse : {false, true}) { + for (ESeek seek : {ESeek::Exact, ESeek::Lower, ESeek::Upper}) { + for (ui32 firstCell : xrange<ui32>(0, part.Stat.Rows / 7 + 1)) { + for (ui32 secondCell : xrange<ui32>(0, 14)) { + TVector<TCell> key = MakeKey(firstCell, secondCell); + + TTouchEnv bTreeEnv, flatEnv; + TPartBtreeIndexIt bTree(&part, &bTreeEnv, { }); + TPartIndexIt flat(&part, &flatEnv, { }); + + TStringBuilder message = TStringBuilder() << (reverse ? "SeekKeyReverse" : "SeekKey") << "(" << seek << ") "; + for (auto c : key) { + message << c.AsValue<ui32>() << " "; + } + + EReady bTreeReady = SeekKey(bTree, bTreeEnv, seek, reverse, key, keyDefaults, message); + EReady flatReady = SeekKey(flat, flatEnv, seek, reverse, key, keyDefaults, message); + AssertEqual(bTree, bTreeReady, flat, flatReady, message, true); + } + } + } + } + } + + void CheckNextPrev(const TPartStore& part) { + for (bool next : {true, false}) { + for (TRowId rowId : xrange(part.Stat.Rows)) { + TTouchEnv bTreeEnv, flatEnv; + TPartBtreeIndexIt bTree(&part, &bTreeEnv, { }); + TPartIndexIt flat(&part, &flatEnv, { }); + + // checking initial seek: + { + TString message = TStringBuilder() << "CheckNext " << rowId; + EReady bTreeReady = SeekRowId(bTree, bTreeEnv, rowId, message); + EReady flatReady = SeekRowId(flat, flatEnv, rowId, message); + UNIT_ASSERT_VALUES_EQUAL(bTreeReady, rowId < part.Stat.Rows ? EReady::Data : EReady::Gone); + AssertEqual(bTree, bTreeReady, flat, flatReady, message); + } + + // checking next: + while (true) + { + TString message = TStringBuilder() << "CheckNext " << rowId << " -> " << rowId; + EReady bTreeReady = NextPrev(bTree, bTreeEnv, next, message); + EReady flatReady = NextPrev(flat, flatEnv, next, message); + AssertEqual(bTree, bTreeReady, flat, flatReady, message); + if (flatReady == EReady::Gone) { + break; + } + } + } + } + } + + void CheckPart(ui32 levels) { + TPartEggs eggs = MakePart(false, levels); + const auto part = *eggs.Lone(); + + CheckSeekRowId(part); + CheckSeekLast(part); + CheckSeekKey(part, eggs.Scheme->Keys.Get()); + CheckNextPrev(part); + } + + Y_UNIT_TEST(NoNodes) { + CheckPart(0); + } + + Y_UNIT_TEST(OneNode) { + CheckPart(1); + } + + Y_UNIT_TEST(FewNodes) { + CheckPart(3); + } +} + +Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { + void DoChargeRowId(ICharge& charge, TTouchEnv& env, const TRowId row1, const TRowId row2, ui64 itemsLimit, ui64 bytesLimit, + bool reverse, const TKeyCellDefaults &keyDefaults, const TString& message, ui32 failsAllowed = 10) { + while (true) { + bool ready = reverse + ? charge.DoReverse(row1, row2, keyDefaults, itemsLimit, bytesLimit) + : charge.Do(row1, row2, keyDefaults, itemsLimit, bytesLimit); + if (ready) { + return; + } + env.LoadTouched(true); + UNIT_ASSERT_C(failsAllowed--, "Too many fails " + message); + } + Y_UNREACHABLE(); + } + + bool DoChargeKeys(ICharge& charge, TTouchEnv& env, const TCells key1, const TCells key2, ui64 itemsLimit, ui64 bytesLimit, + bool reverse, const TKeyCellDefaults &keyDefaults, const TString& message, ui32 failsAllowed = 10) { + while (true) { + auto result = reverse + ? charge.DoReverse(key1, key2, 0, Max<TRowId>(), keyDefaults, itemsLimit, bytesLimit) + : charge.Do(key1, key2, 0, Max<TRowId>(), keyDefaults, itemsLimit, bytesLimit); + if (result.Ready) { + return result.Overshot; + } + env.LoadTouched(true); + UNIT_ASSERT_C(failsAllowed--, "Too many fails " + message); + } + Y_UNREACHABLE(); + } + + void CheckChargeRowId(const TPartStore& part, TTagsRef tags, const TKeyCellDefaults *keyDefaults, bool reverse) { + for (TRowId rowId1 : xrange(part.Stat.Rows + 1)) { + for (TRowId rowId2 : xrange(part.Stat.Rows + 1)) { + TTouchEnv bTreeEnv, flatEnv; + TChargeBTreeIndex bTree(&bTreeEnv, part, tags, true); + TCharge flat(&flatEnv, part, tags, true); + + TString message = TStringBuilder() << (reverse ? "ChargeRowIdReverse " : "ChargeRowId ") << rowId1 << " " << rowId2; + DoChargeRowId(bTree, bTreeEnv, rowId1, rowId2, 0, 0, reverse, *keyDefaults, message); + DoChargeRowId(flat, flatEnv, rowId1, rowId2, 0, 0, reverse, *keyDefaults, message); + AssertLoadTheSame(part, bTreeEnv, flatEnv, message); + } + } + } + + void CheckChargeKeys(const TPartStore& part, TTagsRef tags, const TKeyCellDefaults *keyDefaults, bool reverse) { + for (ui32 firstCellKey1 : xrange<ui32>(0, part.Stat.Rows / 7 + 1)) { + for (ui32 secondCellKey1 : xrange<ui32>(0, 14)) { + for (ui32 firstCellKey2 : xrange<ui32>(0, part.Stat.Rows / 7 + 1)) { + for (ui32 secondCellKey2 : xrange<ui32>(0, 14)) { + TVector<TCell> key1 = MakeKey(firstCellKey1, secondCellKey1); + TVector<TCell> key2 = MakeKey(firstCellKey2, secondCellKey2); + + TTouchEnv bTreeEnv, flatEnv; + TChargeBTreeIndex bTree(&bTreeEnv, part, tags, true); + TCharge flat(&flatEnv, part, tags, true); + + TStringBuilder message = TStringBuilder() << (reverse ? "ChargeKeysReverse " : "ChargeKeys ") << "("; + for (auto c : key1) { + message << c.AsValue<ui32>() << " "; + } + message << ") ("; + for (auto c : key2) { + message << c.AsValue<ui32>() << " "; + } + message << ")"; + + bool bTreeOvershot = DoChargeKeys(bTree, bTreeEnv, key1, key2, 0, 0, reverse, *keyDefaults, message); + bool flatOvershot = DoChargeKeys(flat, flatEnv, key1, key2, 0, 0, reverse, *keyDefaults, message); + + // TODO + // UNIT_ASSERT_VALUES_EQUAL_C(bTreeOvershot, flatOvershot, message); + Y_UNUSED(bTreeOvershot); + Y_UNUSED(flatOvershot); + + AssertLoadTheSame(part, bTreeEnv, flatEnv, message); + } + } + } + } + } + + void CheckPart(ui32 levels) { + TPartEggs eggs = MakePart(false, levels); + const auto part = *eggs.Lone(); + + auto tags = TVector<TTag>(); + for (auto c : eggs.Scheme->Cols) { + tags.push_back(c.Tag); + } + + CheckChargeRowId(part, tags, eggs.Scheme->Keys.Get(), false); + CheckChargeRowId(part, tags, eggs.Scheme->Keys.Get(), true); + // TODO: isn't working yet + // CheckChargeKeys(part, tags, eggs.Scheme->Keys.Get(), false); + // CheckChargeKeys(part, tags, eggs.Scheme->Keys.Get(), true); + // TODO: mixed + } + + Y_UNIT_TEST(NoNodes) { + CheckPart(0); + } + + Y_UNIT_TEST(OneNode) { + CheckPart(1); + } + + Y_UNIT_TEST(FewNodes) { + CheckPart(3); + } +} + +Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { + void AssertEqual(const TRunIt& bTree, EReady bTreeReady, const TRunIt& flat, EReady flatReady, const TString& message) { + UNIT_ASSERT_VALUES_EQUAL_C(bTreeReady, flatReady, message); + UNIT_ASSERT_VALUES_EQUAL_C(bTree.IsValid(), flat.IsValid(), message); + UNIT_ASSERT_VALUES_EQUAL_C(bTree.GetRowId(), flat.GetRowId(), message); + } + + EReady SeekKey(TRunIt& iter, TTouchEnv& env, ESeek seek, bool reverse, TCells key, const TString& message, ui32 failsAllowed = 10) { + return Retry([&]() { + return reverse ? iter.SeekReverse(key, seek) : iter.Seek(key, seek); + }, env, message, failsAllowed); + } + + EReady Next(TRunIt& iter, TTouchEnv& env, bool reverse, const TString& message, ui32 failsAllowed = 10) { + return Retry([&]() { + return reverse ? iter.Prev() : iter.Next(); + }, env, message, failsAllowed); + } + + void CheckIterateKey(const TPartEggs& eggs) { + const auto part = *eggs.Lone(); + + TRun btreeRun(*eggs.Scheme->Keys), flatRun(*eggs.Scheme->Keys); + auto flatPart = part.CloneWithEpoch(part.Epoch); + for (auto& slice : *part.Slices) { + btreeRun.Insert(eggs.Lone(), slice); + auto pages = (TVector<TBtreeIndexMeta>*)&flatPart->IndexPages.BTreeGroups; + pages->clear(); + pages = (TVector<TBtreeIndexMeta>*)&flatPart->IndexPages.BTreeHistoric; + pages->clear(); + flatRun.Insert(flatPart, slice); + } + + auto tags = TVector<TTag>(); + for (auto c : eggs.Scheme->Cols) { + tags.push_back(c.Tag); + } + + for (bool reverse : {false, true}) { + for (ESeek seek : {ESeek::Exact, ESeek::Lower, ESeek::Upper}) { + for (ui32 firstCell : xrange<ui32>(0, part.Stat.Rows / 7 + 1)) { + for (ui32 secondCell : xrange<ui32>(0, 14)) { + TVector<TCell> key = MakeKey(firstCell, secondCell); + + TTouchEnv bTreeEnv, flatEnv; + TRunIt flat(flatRun, tags, eggs.Scheme->Keys, &flatEnv); + TRunIt bTree(btreeRun, tags, eggs.Scheme->Keys, &bTreeEnv); + + { + TStringBuilder message = TStringBuilder() << (reverse ? "SeekKeyReverse" : "SeekKey") << "(" << seek << ") "; + for (auto c : key) { + message << c.AsValue<ui32>() << " "; + } + EReady bTreeReady = SeekKey(bTree, bTreeEnv, seek, reverse, key, message); + EReady flatReady = SeekKey(flat, flatEnv, seek, reverse, key, message); + AssertEqual(bTree, bTreeReady, flat, flatReady, message); + AssertLoadTheSame(part, bTreeEnv, flatEnv, message); + } + + for (ui32 steps = 1; steps <= 10; steps++) { + TStringBuilder message = TStringBuilder() << (reverse ? "SeekKeyReverse" : "SeekKey") << "(" << seek << ") "; + for (auto c : key) { + message << c.AsValue<ui32>() << " "; + } + message << " --> " << steps << " steps "; + EReady bTreeReady = Next(bTree, bTreeEnv, reverse, message); + EReady flatReady = Next(flat, flatEnv, reverse, message); + AssertEqual(bTree, bTreeReady, flat, flatReady, message); + AssertLoadTheSame(part, bTreeEnv, flatEnv, message); + } + } + } + } + } + } + + void CheckPart(bool slices, ui32 levels) { + TPartEggs eggs = MakePart(slices, levels); + const auto part = *eggs.Lone(); + + CheckIterateKey(eggs); + } + + Y_UNIT_TEST(NoNodes_SingleSlice) { + CheckPart(false, 0); + } + + Y_UNIT_TEST(OneNode_SingleSlice) { + CheckPart(false, 1); + } + + Y_UNIT_TEST(OneNode_ManySlices) { + CheckPart(true, 1); + } + + Y_UNIT_TEST(FewNodes_SingleSlice) { + CheckPart(false, 3); + } + + Y_UNIT_TEST(FewNodes_ManySlices) { + CheckPart(true, 3); + } +} + +} diff --git a/ydb/core/tablet_flat/ut/ut_btree_index.cpp b/ydb/core/tablet_flat/ut/ut_btree_index_nodes.cpp index 1ef4a89230..a881183d4e 100644 --- a/ydb/core/tablet_flat/ut/ut_btree_index.cpp +++ b/ydb/core/tablet_flat/ut/ut_btree_index_nodes.cpp @@ -1,8 +1,5 @@ #include "flat_page_btree_index.h" #include "flat_page_btree_index_writer.h" -#include "flat_part_btree_index_iter.h" -#include "flat_part_charge.h" -#include "flat_part_charge_btree_index.h" #include "test/libs/table/test_writer.h" #include <ydb/core/tablet_flat/test/libs/rows/layout.h> #include <library/cpp/testing/unittest/registar.h> @@ -14,36 +11,6 @@ namespace { using TShortChild = TBtreeIndexNode::TShortChild; using TChild = TBtreeIndexNode::TChild; - struct TTouchEnv : public NTest::TTestEnv { - const TSharedData* TryGetPage(const TPart *part, TPageId pageId, TGroupId groupId) override - { - Touched[groupId].insert(pageId); - if (Loaded[groupId].contains(pageId)) { - return NTest::TTestEnv::TryGetPage(part, pageId, groupId); - } - return nullptr; - } - - static void LoadTouched(IPages& env, bool clearHas) { - auto touchEnv = dynamic_cast<TTouchEnv*>(&env); - if (touchEnv) { - auto &has = touchEnv->Loaded; - auto &touched = touchEnv->Touched; - - if (clearHas) { - has.clear(); - } - for (const auto &g : touched) { - has[g.first].insert(g.second.begin(), g.second.end()); - } - touched.clear(); - } - } - - TMap<TGroupId, TSet<TPageId>> Loaded; - TMap<TGroupId, TSet<TPageId>> Touched; - }; - TLayoutCook MakeLayout() { TLayoutCook lay; @@ -906,343 +873,4 @@ Y_UNIT_TEST_SUITE(TBtreeIndexTPart) { } } -Y_UNIT_TEST_SUITE(TPartBtreeIndexIt) { - void AssertEqual(const TPartBtreeIndexIt& bTree, EReady bTreeReady, const TPartIndexIt& flat, EReady flatReady, const TString& message, bool allowFirstLastPageDifference = false) { - // Note: it's possible that B-Tree index don't return Gone status for keys before the first page or keys after the last page - if (allowFirstLastPageDifference && flatReady == EReady::Gone && bTreeReady == EReady::Data && - (bTree.GetRowId() == 0 || bTree.GetNextRowId() == bTree.GetEndRowId())) { - UNIT_ASSERT_C(bTree.IsValid(), message); - return; - } - - UNIT_ASSERT_VALUES_EQUAL_C(bTreeReady, flatReady, message); - UNIT_ASSERT_VALUES_EQUAL_C(bTree.IsValid(), flat.IsValid(), message); - if (flat.IsValid()) { - UNIT_ASSERT_VALUES_EQUAL_C(bTree.GetPageId(), flat.GetPageId(), message); - UNIT_ASSERT_VALUES_EQUAL_C(bTree.GetRowId(), flat.GetRowId(), message); - UNIT_ASSERT_VALUES_EQUAL_C(bTree.GetNextRowId(), flat.GetNextRowId(), message); - } - } - - EReady Retry(std::function<EReady()> action, IPages& env, const TString& message, ui32 failsAllowed = 10) { - while (true) { - if (auto ready = action(); ready != EReady::Page) { - return ready; - } - TTouchEnv::LoadTouched(env, false); - UNIT_ASSERT_C(failsAllowed--, "Too many fails " + message); - } - Y_UNREACHABLE(); - } - - EReady SeekRowId(IIndexIter& iter, IPages& env, TRowId rowId, const TString& message, ui32 failsAllowed = 10) { - return Retry([&]() { - return iter.Seek(rowId); - }, env, message, failsAllowed); - } - - EReady SeekLast(IIndexIter& iter, IPages& env, const TString& message, ui32 failsAllowed = 10) { - return Retry([&]() { - return iter.SeekLast(); - }, env, message, failsAllowed); - } - - EReady SeekKey(IIndexIter& iter, IPages& env, ESeek seek, bool reverse, TCells key, const TKeyCellDefaults *keyDefaults, const TString& message, ui32 failsAllowed = 10) { - return Retry([&]() { - if (reverse) { - return iter.SeekReverse(seek, key, keyDefaults); - } else { - return iter.Seek(seek, key, keyDefaults); - } - }, env, message, failsAllowed); - } - - EReady NextPrev(IIndexIter& iter, IPages& env, bool next, const TString& message, ui32 failsAllowed = 10) { - return Retry([&]() { - if (next) { - return iter.Next(); - } else { - return iter.Prev(); - } - }, env, message, failsAllowed); - } - - template<typename TEnv> - void CheckSeekRowId(const TPartStore& part) { - for (TRowId rowId1 : xrange(part.Stat.Rows + 1)) { - for (TRowId rowId2 : xrange(part.Stat.Rows + 1)) { - TEnv env; - TPartBtreeIndexIt bTree(&part, &env, { }); - TPartIndexIt flat(&part, &env, { }); - - // checking initial seek: - { - TString message = TStringBuilder() << "SeekRowId<" << typeid(TEnv).name() << "> " << rowId1; - EReady bTreeReady = SeekRowId(bTree, env, rowId1, message); - EReady flatReady = SeekRowId(flat, env, rowId1, message); - UNIT_ASSERT_VALUES_EQUAL(bTreeReady, rowId1 < part.Stat.Rows ? EReady::Data : EReady::Gone); - AssertEqual(bTree, bTreeReady, flat, flatReady, message); - } - - // checking repositioning: - { - TString message = TStringBuilder() << "SeekRowId<" << typeid(TEnv).name() << "> " << rowId1 << " -> " << rowId2; - EReady bTreeReady = SeekRowId(bTree, env, rowId2, message); - EReady flatReady = SeekRowId(flat, env, rowId2, message); - UNIT_ASSERT_VALUES_EQUAL(bTreeReady, rowId2 < part.Stat.Rows ? EReady::Data : EReady::Gone); - AssertEqual(bTree, bTreeReady, flat, flatReady, message); - } - } - } - } - - template<typename TEnv> - void CheckSeekLast(const TPartStore& part) { - TEnv env; - TPartBtreeIndexIt bTree(&part, &env, { }); - TPartIndexIt flat(&part, &env, { }); - - TString message = TStringBuilder() << "SeekLast<" << typeid(TEnv).name() << ">"; - EReady bTreeReady = SeekLast(bTree, env, message); - EReady flatReady = SeekLast(flat, env, message); - UNIT_ASSERT_VALUES_EQUAL(bTreeReady, EReady::Data); - AssertEqual(bTree, bTreeReady, flat, flatReady, message); - } - - template<typename TEnv> - void CheckSeekKey(const TPartStore& part, const TKeyCellDefaults *keyDefaults) { - for (bool reverse : {false, true}) { - for (ESeek seek : {ESeek::Exact, ESeek::Lower, ESeek::Upper}) { - for (ui32 keyId : xrange(0u, static_cast<ui32>(part.Stat.Rows) + 2)) { - TVector<TCell> key{TCell::Make(keyId / 7), TCell::Make(keyId % 7)}; - - while (true) { - TEnv env; - TPartBtreeIndexIt bTree(&part, &env, { }); - TPartIndexIt flat(&part, &env, { }); - - TStringBuilder message = TStringBuilder() << (reverse ? "SeekKeyReverse<" : "SeekKey<") << typeid(TEnv).name() << ">(" << seek << ") "; - for (auto c : key) { - message << c.AsValue<ui32>() << " "; - } - - EReady bTreeReady = SeekKey(bTree, env, seek, reverse, key, keyDefaults, message); - EReady flatReady = SeekKey(flat, env, seek, reverse, key, keyDefaults, message); - UNIT_ASSERT_VALUES_EQUAL_C(bTreeReady, key.empty() ? flatReady : EReady::Data, "Can't be exhausted"); - AssertEqual(bTree, bTreeReady, flat, flatReady, message, !key.empty()); - - if (!key) { - break; - } - key.pop_back(); - } - } - } - } - } - - template<typename TEnv> - void CheckNextPrev(const TPartStore& part) { - for (bool next : {true, false}) { - for (TRowId rowId : xrange(part.Stat.Rows)) { - TEnv env; - TPartBtreeIndexIt bTree(&part, &env, { }); - TPartIndexIt flat(&part, &env, { }); - - // checking initial seek: - { - TString message = TStringBuilder() << "CheckNext<" << typeid(TEnv).name() << "> " << rowId; - EReady bTreeReady = SeekRowId(bTree, env, rowId, message); - EReady flatReady = SeekRowId(flat, env, rowId, message); - UNIT_ASSERT_VALUES_EQUAL(bTreeReady, rowId < part.Stat.Rows ? EReady::Data : EReady::Gone); - AssertEqual(bTree, bTreeReady, flat, flatReady, message); - } - - // checking next: - while (true) - { - TString message = TStringBuilder() << "CheckNext<" << typeid(TEnv).name() << "> " << rowId << " -> " << rowId; - EReady bTreeReady = NextPrev(bTree, env, next, message); - EReady flatReady = NextPrev(flat, env, next, message); - AssertEqual(bTree, bTreeReady, flat, flatReady, message); - if (flatReady == EReady::Gone) { - break; - } - } - } - } - } - - void CheckPart(TConf&& conf, ui32 rows, ui32 levels) { - TLayoutCook lay; - - lay - .Col(0, 0, NScheme::NTypeIds::Uint32) - .Col(0, 1, NScheme::NTypeIds::Uint32) - .Key({0, 1}); - - conf.WriteBTreeIndex = true; - TPartCook cook(lay, conf); - - for (ui32 i : xrange(1u, rows + 1)) { - cook.Add(*TSchemedCookRow(*lay).Col(i / 7, i % 7)); - } - - TPartEggs eggs = cook.Finish(); - - const auto part = *eggs.Lone(); - - Cerr << DumpPart(part, 1) << Endl; - - UNIT_ASSERT_VALUES_EQUAL(part.IndexPages.BTreeGroups[0].LevelCount, levels); - - CheckSeekRowId<TTestEnv>(part); - CheckSeekRowId<TTouchEnv>(part); - CheckSeekLast<TTestEnv>(part); - CheckSeekLast<TTouchEnv>(part); - CheckSeekKey<TTestEnv>(part, eggs.Scheme->Keys.Get()); - CheckSeekKey<TTouchEnv>(part, eggs.Scheme->Keys.Get()); - CheckNextPrev<TTestEnv>(part); - CheckNextPrev<TTouchEnv>(part); - } - - Y_UNIT_TEST(NoNodes) { - NPage::TConf conf; - - CheckPart(std::move(conf), 100, 0); - } - - Y_UNIT_TEST(OneNode) { - NPage::TConf conf; - conf.Group(0).PageRows = 2; - - CheckPart(std::move(conf), 100, 1); - } - - Y_UNIT_TEST(FewNodes) { - NPage::TConf conf; - conf.Group(0).PageRows = 2; - conf.Group(0).BTreeIndexNodeKeysMin = 3; - conf.Group(0).BTreeIndexNodeKeysMax = 4; - - CheckPart(std::move(conf), 300, 3); - } -} - -Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { - void AssertEqual(const TPartStore& part, const TMap<TGroupId, TSet<TPageId>>& bTree, const TMap<TGroupId, TSet<TPageId>>& flat, const TString& message) { - TSet<TGroupId> groupIds; - for (const auto &c : {bTree, flat}) { - for (const auto &g : c) { - groupIds.insert(g.first); - } - } - - for (TGroupId groupId : groupIds) { - TSet<TPageId> bTreeDataPages, flatDataPages; - for (TPageId pageId : bTree.Value(groupId, TSet<TPageId>{})) { - if (part.GetPageType(pageId, groupId) == EPage::DataPage) { - bTreeDataPages.insert(pageId); - } - } - for (TPageId pageId : flat.Value(groupId, TSet<TPageId>{})) { - if (part.GetPageType(pageId, groupId) == EPage::DataPage) { - flatDataPages.insert(pageId); - } - } - - UNIT_ASSERT_VALUES_EQUAL_C(flatDataPages, bTreeDataPages, message); - } - } - - void AssertEqual(const TPartStore& part, const TTouchEnv& bTree, const TTouchEnv& flat, const TString& message) { - AssertEqual(part, bTree.Loaded, flat.Loaded, message); - AssertEqual(part, bTree.Touched, flat.Touched, message); - } - - void DoChargeRowId(ICharge& charge, IPages& env, const TRowId row1, const TRowId row2, ui64 itemsLimit, ui64 bytesLimit, - bool reverse, const TKeyCellDefaults &keyDefaults, const TString& message, ui32 failsAllowed = 10) { - while (true) { - bool ready = reverse - ? charge.DoReverse(row1, row2, keyDefaults, itemsLimit, bytesLimit) - : charge.Do(row1, row2, keyDefaults, itemsLimit, bytesLimit); - if (ready) { - return; - } - TTouchEnv::LoadTouched(env, false); - UNIT_ASSERT_C(failsAllowed--, "Too many fails " + message); - } - Y_UNREACHABLE(); - } - - void CheckChargeRowId(const TPartStore& part, TTagsRef tags, const TKeyCellDefaults *keyDefaults, bool reverse) { - for (TRowId rowId1 : xrange(part.Stat.Rows + 1)) { - for (TRowId rowId2 : xrange(part.Stat.Rows + 1)) { - TTouchEnv bTreeEnv, flatEnv; - TChargeBTreeIndex bTree(&bTreeEnv, part, tags, true); - TCharge flat(&flatEnv, part, tags, true); - - TString message = TStringBuilder() << (reverse ? "ChargeRowIdReverse " : "ChargeRowId ") << rowId1 << " " << rowId2; - DoChargeRowId(bTree, bTreeEnv, rowId1, rowId2, 0, 0, reverse, *keyDefaults, message); - DoChargeRowId(flat, flatEnv, rowId1, rowId2, 0, 0, reverse, *keyDefaults, message); - AssertEqual(part, bTreeEnv, flatEnv, message); - } - } - } - - void CheckPart(TConf&& conf, ui32 rows, ui32 levels) { - TLayoutCook lay; - - lay - .Col(0, 0, NScheme::NTypeIds::Uint32) - .Col(0, 1, NScheme::NTypeIds::Uint32) - .Key({0, 1}); - - conf.WriteBTreeIndex = true; - TPartCook cook(lay, conf); - - for (ui32 i : xrange(1u, rows + 1)) { - cook.Add(*TSchemedCookRow(*lay).Col(i / 7, i % 7)); - } - - TPartEggs eggs = cook.Finish(); - - const auto part = *eggs.Lone(); - - Cerr << DumpPart(part, 1) << Endl; - - UNIT_ASSERT_VALUES_EQUAL(part.IndexPages.BTreeGroups[0].LevelCount, levels); - - auto tags = TVector<TTag>(); - for (auto c : eggs.Scheme->Cols) { - tags.push_back(c.Tag); - } - - CheckChargeRowId(part, tags, eggs.Scheme->Keys.Get(), false); - CheckChargeRowId(part, tags, eggs.Scheme->Keys.Get(), true); - } - - Y_UNIT_TEST(NoNodes) { - NPage::TConf conf; - - CheckPart(std::move(conf), 100, 0); - } - - Y_UNIT_TEST(OneNode) { - NPage::TConf conf; - conf.Group(0).PageRows = 2; - - CheckPart(std::move(conf), 100, 1); - } - - Y_UNIT_TEST(FewNodes) { - NPage::TConf conf; - conf.Group(0).PageRows = 2; - conf.Group(0).BTreeIndexNodeKeysMin = 3; - conf.Group(0).BTreeIndexNodeKeysMax = 4; - - CheckPart(std::move(conf), 300, 3); - } -} - } diff --git a/ydb/core/tablet_flat/ut/ya.make b/ydb/core/tablet_flat/ut/ya.make index 1dea88ef76..1103aaa188 100644 --- a/ydb/core/tablet_flat/ut/ya.make +++ b/ydb/core/tablet_flat/ut/ya.make @@ -28,7 +28,8 @@ SRCS( flat_test_db.cpp flat_test_db_helpers.h shared_handle_ut.cpp - ut_btree_index.cpp + ut_btree_index_nodes.cpp + ut_btree_index_iter_charge.cpp ut_self.cpp ut_iterator.cpp ut_memtable.cpp |