aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkungurtsev <kungasc@ydb.tech>2024-01-17 14:52:26 +0100
committerGitHub <noreply@github.com>2024-01-17 14:52:26 +0100
commit5fa4b2f5ec340934d41d70a0ebc24375f12e92d8 (patch)
tree2e3fda10a9575768550c338f57c5a022dcb28418
parent74fd99e8a690c4917ebca6d8d1ef637745951a04 (diff)
downloadydb-5fa4b2f5ec340934d41d70a0ebc24375f12e92d8.tar.gz
KIKIMR-19522 BTreeIndex Iter Remove single run optimization (#995)
-rw-r--r--ydb/core/tablet_flat/benchmark/b_part_index.cpp10
-rw-r--r--ydb/core/tablet_flat/flat_part_iter_multi.h76
-rw-r--r--ydb/core/tablet_flat/test/libs/table/test_part.h5
-rw-r--r--ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp570
-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.make3
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