From e08cb14cac5fd7cfd6890514cce14ccc8edc6e0a Mon Sep 17 00:00:00 2001 From: kungasc Date: Mon, 15 Jan 2024 10:24:49 +0000 Subject: [PATCH 01/20] remove run.size=1 optimization --- ydb/core/tablet_flat/flat_part_iter_multi.h | 76 +-- .../ut/ut_btree_index_iter_charge.cpp | 513 ++++++++++++++++++ ...ree_index.cpp => ut_btree_index_nodes.cpp} | 372 ------------- ydb/core/tablet_flat/ut/ya.make | 3 +- 4 files changed, 543 insertions(+), 421 deletions(-) create mode 100644 ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp rename ydb/core/tablet_flat/ut/{ut_btree_index.cpp => ut_btree_index_nodes.cpp} (66%) diff --git a/ydb/core/tablet_flat/flat_part_iter_multi.h b/ydb/core/tablet_flat/flat_part_iter_multi.h index e7149126343e..a058804a47e7 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/ut/ut_btree_index_iter_charge.cpp b/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp new file mode 100644 index 000000000000..c6b6e17dae1f --- /dev/null +++ b/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp @@ -0,0 +1,513 @@ +#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 "test/libs/table/test_writer.h" +#include "test/libs/table/wrap_part.h" +#include +#include + +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> Loaded; + TMap> Touched; + }; + + void AssertTouchedTheSame(const TPartStore& part, const TMap>& bTree, const TMap>& flat, const TString& message) { + TSet groupIds; + for (const auto &c : {bTree, flat}) { + for (const auto &g : c) { + groupIds.insert(g.first); + } + } + + for (TGroupId groupId : groupIds) { + TSet bTreeDataPages, flatDataPages; + for (TPageId pageId : bTree.Value(groupId, TSet{})) { + if (part.GetPageType(pageId, groupId) == EPage::DataPage) { + bTreeDataPages.insert(pageId); + } + } + for (TPageId pageId : flat.Value(groupId, TSet{})) { + if (part.GetPageType(pageId, groupId) == EPage::DataPage) { + flatDataPages.insert(pageId); + } + } + + UNIT_ASSERT_VALUES_EQUAL_C(flatDataPages, bTreeDataPages, message); + } + } + + void AssertTouchedTheSame(const TPartStore& part, const TTouchEnv& bTree, const TTouchEnv& flat, const TString& message) { + AssertTouchedTheSame(part, bTree.Loaded, flat.Loaded, message); + AssertTouchedTheSame(part, bTree.Touched, flat.Touched, message); + } + + TPartEggs MakePart(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); + + // making part with key gaps + const TVector secondCells = {1, 3, 4, 6, 7, 8, 10}; + for (ui32 i : xrange(0u, rows)) { + cook.Add(*TSchemedCookRow(*lay).Col(i / 7, secondCells[i % 7])); + } + + TPartEggs eggs = cook.Finish(); + + const auto part = *eggs.Lone(); + + UNIT_ASSERT_VALUES_EQUAL(part.Slices->size(), 1); + Cerr << DumpPart(part, 3) << Endl; + + UNIT_ASSERT_VALUES_EQUAL(part.IndexPages.BTreeGroups[0].LevelCount, levels); + + return eggs; + } + + TVector 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 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(0, part.Stat.Rows / 7 + 1)) { + for (ui32 secondCell : xrange(0, 14)) { + TVector 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() << " "; + } + + 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(TConf&& conf, ui32 rows, ui32 levels) { + TPartEggs eggs = MakePart(std::move(conf), rows, levels); + const auto part = *eggs.Lone(); + + CheckSeekRowId(part); + CheckSeekLast(part); + CheckSeekKey(part, eggs.Scheme->Keys.Get()); + CheckNextPrev(part); + } + + Y_UNIT_TEST(NoNodes) { + NPage::TConf conf; + + CheckPart(std::move(conf), 40, 0); + } + + Y_UNIT_TEST(OneNode) { + NPage::TConf conf; + conf.Group(0).PageRows = 2; + + CheckPart(std::move(conf), 40, 1); + } + + Y_UNIT_TEST(FewNodes) { + NPage::TConf conf; + conf.Group(0).PageRows = 2; + conf.Group(0).BTreeIndexNodeKeysMin = conf.Group(0).BTreeIndexNodeKeysMax = 2; + + CheckPart(std::move(conf), 40, 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(), keyDefaults, itemsLimit, bytesLimit) + : charge.Do(key1, key2, 0, Max(), 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); + AssertTouchedTheSame(part, bTreeEnv, flatEnv, message); + } + } + } + + void CheckChargeKeys(const TPartStore& part, TTagsRef tags, const TKeyCellDefaults *keyDefaults, bool reverse) { + for (ui32 firstCellKey1 : xrange(0, part.Stat.Rows / 7 + 1)) { + for (ui32 secondCellKey1 : xrange(0, 14)) { + for (ui32 firstCellKey2 : xrange(0, part.Stat.Rows / 7 + 1)) { + for (ui32 secondCellKey2 : xrange(0, 14)) { + TVector key1 = MakeKey(firstCellKey1, secondCellKey1); + TVector 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() << " "; + } + message << ") ("; + for (auto c : key2) { + message << c.AsValue() << " "; + } + 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); + + AssertTouchedTheSame(part, bTreeEnv, flatEnv, message); + } + } + } + } + } + + void CheckPart(TConf&& conf, ui32 rows, ui32 levels) { + TPartEggs eggs = MakePart(std::move(conf), rows, levels); + const auto part = *eggs.Lone(); + + auto tags = TVector(); + 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) { + NPage::TConf conf; + + CheckPart(std::move(conf), 40, 0); + } + + Y_UNIT_TEST(OneNode) { + NPage::TConf conf; + conf.Group(0).PageRows = 2; + + CheckPart(std::move(conf), 40, 1); + } + + Y_UNIT_TEST(FewNodes) { + NPage::TConf conf; + conf.Group(0).PageRows = 2; + conf.Group(0).BTreeIndexNodeKeysMin = conf.Group(0).BTreeIndexNodeKeysMax = 2; + + CheckPart(std::move(conf), 40, 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); + } + + void CheckSeekKey(const TPartEggs& eggs) { + const auto part = *eggs.Lone(); + + TRun btreeRun(*eggs.Scheme->Keys), flatRun(*eggs.Scheme->Keys); + for (auto& slice : *part.Slices) { + btreeRun.Insert(eggs.Lone(), slice); + + auto flatPart = part.CloneWithEpoch(part.Epoch); + auto pages = (TVector*)&flatPart->IndexPages.BTreeGroups; + pages->clear(); + pages = (TVector*)&flatPart->IndexPages.BTreeHistoric; + pages->clear(); + flatRun.Insert(flatPart, slice); + } + + auto tags = TVector(); + 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(0, part.Stat.Rows / 7 + 1)) { + for (ui32 secondCell : xrange(0, 14)) { + TVector 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() << " "; + } + + EReady bTreeReady = SeekKey(bTree, bTreeEnv, seek, reverse, key, message); + EReady flatReady = SeekKey(flat, flatEnv, seek, reverse, key, message); + AssertEqual(bTree, bTreeReady, flat, flatReady, message); + AssertTouchedTheSame(part, bTreeEnv, flatEnv, message); + } + } + } + } + } + + void CheckPart(TConf&& conf, ui32 rows, ui32 levels) { + TPartEggs eggs = MakePart(std::move(conf), rows, levels); + const auto part = *eggs.Lone(); + + CheckSeekKey(eggs); + } + + Y_UNIT_TEST(NoNodes) { + NPage::TConf conf; + + CheckPart(std::move(conf), 40, 0); + } + + Y_UNIT_TEST(OneNode) { + NPage::TConf conf; + conf.Group(0).PageRows = 2; + + CheckPart(std::move(conf), 40, 1); + } + + Y_UNIT_TEST(FewNodes) { + NPage::TConf conf; + conf.Group(0).PageRows = 2; + conf.Group(0).BTreeIndexNodeKeysMin = conf.Group(0).BTreeIndexNodeKeysMax = 2; + + CheckPart(std::move(conf), 40, 3); + } +} + +} diff --git a/ydb/core/tablet_flat/ut/ut_btree_index.cpp b/ydb/core/tablet_flat/ut/ut_btree_index_nodes.cpp similarity index 66% rename from ydb/core/tablet_flat/ut/ut_btree_index.cpp rename to ydb/core/tablet_flat/ut/ut_btree_index_nodes.cpp index 1ef4a8923013..a881183d4efa 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 #include @@ -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(&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> Loaded; - TMap> 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 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 - 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 - 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 - 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(part.Stat.Rows) + 2)) { - TVector 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() << " "; - } - - 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 - 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(part); - CheckSeekRowId(part); - CheckSeekLast(part); - CheckSeekLast(part); - CheckSeekKey(part, eggs.Scheme->Keys.Get()); - CheckSeekKey(part, eggs.Scheme->Keys.Get()); - CheckNextPrev(part); - CheckNextPrev(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>& bTree, const TMap>& flat, const TString& message) { - TSet groupIds; - for (const auto &c : {bTree, flat}) { - for (const auto &g : c) { - groupIds.insert(g.first); - } - } - - for (TGroupId groupId : groupIds) { - TSet bTreeDataPages, flatDataPages; - for (TPageId pageId : bTree.Value(groupId, TSet{})) { - if (part.GetPageType(pageId, groupId) == EPage::DataPage) { - bTreeDataPages.insert(pageId); - } - } - for (TPageId pageId : flat.Value(groupId, TSet{})) { - 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(); - 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 1dea88ef7694..1103aaa188de 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 From 6a5b0195d8af743afcec9c61dbe1aba15452911d Mon Sep 17 00:00:00 2001 From: kungasc Date: Mon, 15 Jan 2024 14:26:11 +0000 Subject: [PATCH 02/20] bench with seek modes --- ydb/core/tablet_flat/benchmark/b_part_index.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ydb/core/tablet_flat/benchmark/b_part_index.cpp b/ydb/core/tablet_flat/benchmark/b_part_index.cpp index c991a2d13fff..0f55324cd027 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(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); From 7ea262532a2c95b21377a37777b9514198ec63a5 Mon Sep 17 00:00:00 2001 From: kungasc Date: Mon, 15 Jan 2024 14:27:08 +0000 Subject: [PATCH 03/20] wip test with slices --- .../ut/ut_btree_index_iter_charge.cpp | 94 ++++++++++++++----- 1 file changed, 72 insertions(+), 22 deletions(-) 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 index c6b6e17dae1f..aceb306ef231 100644 --- a/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp +++ b/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp @@ -68,7 +68,7 @@ namespace { AssertTouchedTheSame(part, bTree.Touched, flat.Touched, message); } - TPartEggs MakePart(TConf&& conf, ui32 rows, ui32 levels) { + TPartEggs MakePart(TConf&& conf, bool runs, ui32 rows, ui32 levels) { TLayoutCook lay; lay @@ -89,7 +89,17 @@ namespace { const auto part = *eggs.Lone(); - UNIT_ASSERT_VALUES_EQUAL(part.Slices->size(), 1); + if (runs) { + 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); @@ -270,7 +280,7 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIt) { } void CheckPart(TConf&& conf, ui32 rows, ui32 levels) { - TPartEggs eggs = MakePart(std::move(conf), rows, levels); + TPartEggs eggs = MakePart(std::move(conf), false, rows, levels); const auto part = *eggs.Lone(); CheckSeekRowId(part); @@ -385,7 +395,7 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { } void CheckPart(TConf&& conf, ui32 rows, ui32 levels) { - TPartEggs eggs = MakePart(std::move(conf), rows, levels); + TPartEggs eggs = MakePart(std::move(conf), false, rows, levels); const auto part = *eggs.Lone(); auto tags = TVector(); @@ -436,7 +446,13 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { }, env, message, failsAllowed); } - void CheckSeekKey(const TPartEggs& eggs) { + 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); @@ -466,47 +482,81 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { 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() << " "; + { + TStringBuilder message = TStringBuilder() << (reverse ? "SeekKeyReverse" : "SeekKey") << "(" << seek << ") "; + for (auto c : key) { + message << c.AsValue() << " "; + } + EReady bTreeReady = SeekKey(bTree, bTreeEnv, seek, reverse, key, message); + EReady flatReady = SeekKey(flat, flatEnv, seek, reverse, key, message); + AssertEqual(bTree, bTreeReady, flat, flatReady, message); + AssertTouchedTheSame(part, bTreeEnv, flatEnv, message); + } + + for (ui32 steps = 1; steps < 3; steps++) { + TStringBuilder message = TStringBuilder() << (reverse ? "SeekKeyReverse" : "SeekKey") << "(" << seek << ") "; + for (auto c : key) { + message << c.AsValue() << " "; + } + message << " --> " << steps << " steps "; + EReady bTreeReady = Next(bTree, bTreeEnv, reverse, message); + EReady flatReady = Next(flat, flatEnv, reverse, message); + AssertEqual(bTree, bTreeReady, flat, flatReady, message); + AssertTouchedTheSame(part, bTreeEnv, flatEnv, message); } - - EReady bTreeReady = SeekKey(bTree, bTreeEnv, seek, reverse, key, message); - EReady flatReady = SeekKey(flat, flatEnv, seek, reverse, key, message); - AssertEqual(bTree, bTreeReady, flat, flatReady, message); - AssertTouchedTheSame(part, bTreeEnv, flatEnv, message); } } } } } - void CheckPart(TConf&& conf, ui32 rows, ui32 levels) { - TPartEggs eggs = MakePart(std::move(conf), rows, levels); + void CheckPart(TConf&& conf, bool runs, ui32 rows, ui32 levels) { + TPartEggs eggs = MakePart(std::move(conf), runs, rows, levels); const auto part = *eggs.Lone(); - CheckSeekKey(eggs); + CheckIterateKey(eggs); } - Y_UNIT_TEST(NoNodes) { + Y_UNIT_TEST(NoNodes_SingleRun) { NPage::TConf conf; - CheckPart(std::move(conf), 40, 0); + CheckPart(std::move(conf), false, 40, 0); } - Y_UNIT_TEST(OneNode) { + Y_UNIT_TEST(OneNode_SingleRun) { NPage::TConf conf; conf.Group(0).PageRows = 2; - CheckPart(std::move(conf), 40, 1); + CheckPart(std::move(conf), false, 40, 1); } - Y_UNIT_TEST(FewNodes) { + Y_UNIT_TEST(FewNodes_SingleRun) { NPage::TConf conf; conf.Group(0).PageRows = 2; conf.Group(0).BTreeIndexNodeKeysMin = conf.Group(0).BTreeIndexNodeKeysMax = 2; - CheckPart(std::move(conf), 40, 3); + CheckPart(std::move(conf), false, 40, 3); + } + + Y_UNIT_TEST(NoNodes_ManyRuns) { + NPage::TConf conf; + + CheckPart(std::move(conf), true, 40, 0); + } + + Y_UNIT_TEST(OneNode_ManyRuns) { + NPage::TConf conf; + conf.Group(0).PageRows = 2; + + CheckPart(std::move(conf), true, 40, 1); + } + + Y_UNIT_TEST(FewNodes_ManyRuns) { + NPage::TConf conf; + conf.Group(0).PageRows = 2; + conf.Group(0).BTreeIndexNodeKeysMin = conf.Group(0).BTreeIndexNodeKeysMax = 2; + + CheckPart(std::move(conf), true, 40, 3); } } From c6b17587a6e7d6d1ae8d7acd23ebffe6f385879c Mon Sep 17 00:00:00 2001 From: kungasc Date: Mon, 15 Jan 2024 16:35:59 +0000 Subject: [PATCH 04/20] cook slices --- .../ut/ut_btree_index_iter_charge.cpp | 147 ++++++++++-------- 1 file changed, 80 insertions(+), 67 deletions(-) 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 index aceb306ef231..9cfdcd30479f 100644 --- a/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp +++ b/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp @@ -2,8 +2,8 @@ #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 "test/libs/table/wrap_part.h" #include #include @@ -68,7 +68,22 @@ namespace { AssertTouchedTheSame(part, bTree.Touched, flat.Touched, message); } - TPartEggs MakePart(TConf&& conf, bool runs, ui32 rows, ui32 levels) { + 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 @@ -77,11 +92,12 @@ namespace { .Key({0, 1}); conf.WriteBTreeIndex = true; + TPartCook cook(lay, conf); // making part with key gaps const TVector secondCells = {1, 3, 4, 6, 7, 8, 10}; - for (ui32 i : xrange(0u, rows)) { + for (ui32 i : xrange(0u, 40u)) { cook.Add(*TSchemedCookRow(*lay).Col(i / 7, secondCells[i % 7])); } @@ -89,12 +105,49 @@ namespace { const auto part = *eggs.Lone(); - if (runs) { + if (slices) { + TSlices slices; + auto partSlices = (TSlices*)part.Slices.Get(); + + auto getKey = [&] (const NPage::TIndex::TRecord* record) { + TSmallVec key; + for (const auto& info : part.Scheme->Groups[0].ColsKeyIdx) { + key.push_back(record->Cell(info)); + } + return TSerializedCellVec(key); + }; + auto add = [&](TRowId pageId1 /*inclusive*/, TRowId pageId2 /*exclusive*/) { + TSlice slice; + slice.FirstInclusive = true; + slice.FirstRowId = pageId1 * 2; + slice.FirstKey = pageId1 > 0 ? getKey(IndexTools::GetRecord(part, pageId1)) : partSlices->begin()->FirstKey; + slice.LastInclusive = false; + slice.LastRowId = pageId2 * 2; + slice.LastKey = pageId2 < 20 ? getKey(IndexTools::GetRecord(part, pageId2)) : 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, 18); + 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; + Cerr << "Slices" << Endl; for (const auto &slice : *part.Slices) { Cerr << " | "; slice.Describe(Cerr); @@ -279,8 +332,8 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIt) { } } - void CheckPart(TConf&& conf, ui32 rows, ui32 levels) { - TPartEggs eggs = MakePart(std::move(conf), false, rows, levels); + void CheckPart(ui32 levels) { + TPartEggs eggs = MakePart(false, levels); const auto part = *eggs.Lone(); CheckSeekRowId(part); @@ -290,24 +343,15 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIt) { } Y_UNIT_TEST(NoNodes) { - NPage::TConf conf; - - CheckPart(std::move(conf), 40, 0); + CheckPart(0); } Y_UNIT_TEST(OneNode) { - NPage::TConf conf; - conf.Group(0).PageRows = 2; - - CheckPart(std::move(conf), 40, 1); + CheckPart(1); } Y_UNIT_TEST(FewNodes) { - NPage::TConf conf; - conf.Group(0).PageRows = 2; - conf.Group(0).BTreeIndexNodeKeysMin = conf.Group(0).BTreeIndexNodeKeysMax = 2; - - CheckPart(std::move(conf), 40, 3); + CheckPart(3); } } @@ -394,8 +438,8 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { } } - void CheckPart(TConf&& conf, ui32 rows, ui32 levels) { - TPartEggs eggs = MakePart(std::move(conf), false, rows, levels); + void CheckPart(ui32 levels) { + TPartEggs eggs = MakePart(false, levels); const auto part = *eggs.Lone(); auto tags = TVector(); @@ -412,24 +456,15 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { } Y_UNIT_TEST(NoNodes) { - NPage::TConf conf; - - CheckPart(std::move(conf), 40, 0); + CheckPart(0); } Y_UNIT_TEST(OneNode) { - NPage::TConf conf; - conf.Group(0).PageRows = 2; - - CheckPart(std::move(conf), 40, 1); + CheckPart(1); } Y_UNIT_TEST(FewNodes) { - NPage::TConf conf; - conf.Group(0).PageRows = 2; - conf.Group(0).BTreeIndexNodeKeysMin = conf.Group(0).BTreeIndexNodeKeysMax = 2; - - CheckPart(std::move(conf), 40, 3); + CheckPart(3); } } @@ -510,53 +545,31 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { } } - void CheckPart(TConf&& conf, bool runs, ui32 rows, ui32 levels) { - TPartEggs eggs = MakePart(std::move(conf), runs, rows, levels); + void CheckPart(bool slices, ui32 levels) { + TPartEggs eggs = MakePart(slices, levels); const auto part = *eggs.Lone(); CheckIterateKey(eggs); } - Y_UNIT_TEST(NoNodes_SingleRun) { - NPage::TConf conf; - - CheckPart(std::move(conf), false, 40, 0); + Y_UNIT_TEST(NoNodes_SingleSlice) { + CheckPart(false, 0); } - Y_UNIT_TEST(OneNode_SingleRun) { - NPage::TConf conf; - conf.Group(0).PageRows = 2; - - CheckPart(std::move(conf), false, 40, 1); + Y_UNIT_TEST(OneNode_SingleSlice) { + CheckPart(false, 1); } - Y_UNIT_TEST(FewNodes_SingleRun) { - NPage::TConf conf; - conf.Group(0).PageRows = 2; - conf.Group(0).BTreeIndexNodeKeysMin = conf.Group(0).BTreeIndexNodeKeysMax = 2; - - CheckPart(std::move(conf), false, 40, 3); + Y_UNIT_TEST(OneNode_ManySlices) { + CheckPart(true, 1); } - Y_UNIT_TEST(NoNodes_ManyRuns) { - NPage::TConf conf; - - CheckPart(std::move(conf), true, 40, 0); - } - - Y_UNIT_TEST(OneNode_ManyRuns) { - NPage::TConf conf; - conf.Group(0).PageRows = 2; - - CheckPart(std::move(conf), true, 40, 1); + Y_UNIT_TEST(FewNodes_SingleSlice) { + CheckPart(false, 3); } - Y_UNIT_TEST(FewNodes_ManyRuns) { - NPage::TConf conf; - conf.Group(0).PageRows = 2; - conf.Group(0).BTreeIndexNodeKeysMin = conf.Group(0).BTreeIndexNodeKeysMax = 2; - - CheckPart(std::move(conf), true, 40, 3); + Y_UNIT_TEST(FewNodes_ManySlices) { + CheckPart(true, 3); } } From 0208b857f5f8d1becba5f7c1416c37d64a17058b Mon Sep 17 00:00:00 2001 From: kungasc Date: Mon, 15 Jan 2024 18:37:43 +0000 Subject: [PATCH 05/20] fix tests --- .../tablet_flat/test/libs/table/test_part.h | 5 +-- .../ut/ut_btree_index_iter_charge.cpp | 38 ++++++++----------- 2 files changed, 18 insertions(+), 25 deletions(-) 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 f9f8458f6b4f..f67cb654caf5 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 index 9cfdcd30479f..3c5acdb18eac 100644 --- a/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp +++ b/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp @@ -38,9 +38,9 @@ namespace { TMap> Touched; }; - void AssertTouchedTheSame(const TPartStore& part, const TMap>& bTree, const TMap>& flat, const TString& message) { + void AssertLoadTheSame(const TPartStore& part, const TTouchEnv& bTree, const TTouchEnv& flat, const TString& message) { TSet groupIds; - for (const auto &c : {bTree, flat}) { + for (const auto &c : {bTree.Loaded, flat.Loaded}) { for (const auto &g : c) { groupIds.insert(g.first); } @@ -48,12 +48,12 @@ namespace { for (TGroupId groupId : groupIds) { TSet bTreeDataPages, flatDataPages; - for (TPageId pageId : bTree.Value(groupId, TSet{})) { + for (TPageId pageId : bTree.Loaded.Value(groupId, TSet{})) { if (part.GetPageType(pageId, groupId) == EPage::DataPage) { bTreeDataPages.insert(pageId); } } - for (TPageId pageId : flat.Value(groupId, TSet{})) { + for (TPageId pageId : flat.Loaded.Value(groupId, TSet{})) { if (part.GetPageType(pageId, groupId) == EPage::DataPage) { flatDataPages.insert(pageId); } @@ -63,11 +63,6 @@ namespace { } } - void AssertTouchedTheSame(const TPartStore& part, const TTouchEnv& bTree, const TTouchEnv& flat, const TString& message) { - AssertTouchedTheSame(part, bTree.Loaded, flat.Loaded, message); - AssertTouchedTheSame(part, bTree.Touched, flat.Touched, message); - } - TPartEggs MakePart(bool slices, ui32 levels) { NPage::TConf conf; switch (levels) { @@ -116,14 +111,14 @@ namespace { } return TSerializedCellVec(key); }; - auto add = [&](TRowId pageId1 /*inclusive*/, TRowId pageId2 /*exclusive*/) { + auto add = [&](TRowId pageIndex1 /*inclusive*/, TRowId pageIndex2 /*exclusive*/) { TSlice slice; slice.FirstInclusive = true; - slice.FirstRowId = pageId1 * 2; - slice.FirstKey = pageId1 > 0 ? getKey(IndexTools::GetRecord(part, pageId1)) : partSlices->begin()->FirstKey; + slice.FirstRowId = pageIndex1 * 2; + slice.FirstKey = pageIndex1 > 0 ? getKey(IndexTools::GetRecord(part, pageIndex1)) : partSlices->begin()->FirstKey; slice.LastInclusive = false; - slice.LastRowId = pageId2 * 2; - slice.LastKey = pageId2 < 20 ? getKey(IndexTools::GetRecord(part, pageId2)) : partSlices->begin()->LastKey; + slice.LastRowId = pageIndex2 * 2; + slice.LastKey = pageIndex2 < 20 ? getKey(IndexTools::GetRecord(part, pageIndex2)) : partSlices->begin()->LastKey; slices.push_back(slice); }; add(0, 2); @@ -133,7 +128,7 @@ namespace { add(8, 9); add(10, 14); add(16, 17); - add(17, 18); + add(17, 19); add(19, 20); partSlices->clear(); @@ -396,7 +391,7 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { 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); - AssertTouchedTheSame(part, bTreeEnv, flatEnv, message); + AssertLoadTheSame(part, bTreeEnv, flatEnv, message); } } } @@ -431,7 +426,7 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { Y_UNUSED(bTreeOvershot); Y_UNUSED(flatOvershot); - AssertTouchedTheSame(part, bTreeEnv, flatEnv, message); + AssertLoadTheSame(part, bTreeEnv, flatEnv, message); } } } @@ -491,10 +486,9 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { 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 flatPart = part.CloneWithEpoch(part.Epoch); auto pages = (TVector*)&flatPart->IndexPages.BTreeGroups; pages->clear(); pages = (TVector*)&flatPart->IndexPages.BTreeHistoric; @@ -525,10 +519,10 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { EReady bTreeReady = SeekKey(bTree, bTreeEnv, seek, reverse, key, message); EReady flatReady = SeekKey(flat, flatEnv, seek, reverse, key, message); AssertEqual(bTree, bTreeReady, flat, flatReady, message); - AssertTouchedTheSame(part, bTreeEnv, flatEnv, message); + AssertLoadTheSame(part, bTreeEnv, flatEnv, message); } - for (ui32 steps = 1; steps < 3; steps++) { + for (ui32 steps = 1; steps <= 10; steps++) { TStringBuilder message = TStringBuilder() << (reverse ? "SeekKeyReverse" : "SeekKey") << "(" << seek << ") "; for (auto c : key) { message << c.AsValue() << " "; @@ -537,7 +531,7 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { EReady bTreeReady = Next(bTree, bTreeEnv, reverse, message); EReady flatReady = Next(flat, flatEnv, reverse, message); AssertEqual(bTree, bTreeReady, flat, flatReady, message); - AssertTouchedTheSame(part, bTreeEnv, flatEnv, message); + AssertLoadTheSame(part, bTreeEnv, flatEnv, message); } } } From aac08df103d2a23e9d2c5131d46ae952a162f6cc Mon Sep 17 00:00:00 2001 From: kungasc Date: Wed, 17 Jan 2024 13:53:09 +0000 Subject: [PATCH 06/20] copy simple solution --- .../flat_part_charge_btree_index.h | 175 ++++++++++++++---- .../tablet_flat/flat_part_charge_range.cpp | 42 ++--- .../ut/ut_btree_index_iter_charge.cpp | 85 ++++++++- 3 files changed, 232 insertions(+), 70 deletions(-) diff --git a/ydb/core/tablet_flat/flat_part_charge_btree_index.h b/ydb/core/tablet_flat/flat_part_charge_btree_index.h index 5876cc2eea65..e07709f111d3 100644 --- a/ydb/core/tablet_flat/flat_part_charge_btree_index.h +++ b/ydb/core/tablet_flat/flat_part_charge_btree_index.h @@ -11,10 +11,33 @@ class TChargeBTreeIndex : public ICharge { using TBtreeIndexMeta = NPage::TBtreeIndexMeta; using TRecIdx = NPage::TRecIdx; using TGroupId = NPage::TGroupId; + using TChild = TBtreeIndexNode::TChild; + using TColumns = TBtreeIndexNode::TColumns; + using TCellsIterable = TBtreeIndexNode::TCellsIterable; + + struct TNodeState { + TChild Meta; + TBtreeIndexNode Node; + TRowId BeginRowId; + TRowId EndRowId; + TCellsIterable BeginKey; + TCellsIterable EndKey; + + TNodeState(TChild meta, TBtreeIndexNode node, TRowId beginRowId, TRowId endRowId, TCellsIterable beginKey, TCellsIterable endKey) + : Meta(meta) + , Node(node) + , BeginRowId(beginRowId) + , EndRowId(endRowId) + , BeginKey(beginKey) + , EndKey(endKey) + { + } + }; public: TChargeBTreeIndex(IPages *env, const TPart &part, TTagsRef tags, bool includeHistory = false) : Part(&part) + , Scheme(*Part->Scheme) , Env(env) { Y_UNUSED(env); Y_UNUSED(part); @@ -23,7 +46,7 @@ class TChargeBTreeIndex : public ICharge { } public: - TResult Do(const TCells key1, const TCells key2, TRowId row1, TRowId row2, + TResult Do(TCells key1, TCells key2, TRowId row1, TRowId row2, const TKeyCellDefaults &keyDefaults, ui64 itemsLimit, ui64 bytesLimit) const noexcept override { bool ready = true; @@ -41,22 +64,27 @@ class TChargeBTreeIndex : public ICharge { if (Y_UNLIKELY(row1 > row2)) { row2 = row1; // will not go further than row1 } + if (Y_UNLIKELY(key1 && key2 && Compare(key1, key2, keyDefaults) > 0)) { + key2 = key1; // will not go further than key1 + } - TVector level, nextLevel(Reserve(2)); + TVector level, nextLevel(Reserve(3)); for (ui32 height = 0; height < meta.LevelCount && ready; height++) { if (height == 0) { - ready &= TryLoadNode(meta.PageId, nextLevel); + ready &= TryLoadRoot(meta, nextLevel); } else { for (ui32 i : xrange(level.size())) { - TRecIdx from = 0, to = level[i].GetKeysCount(); - if (i == 0) { - from = level[i].Seek(row1); + TRecIdx from = 0, to = level[i].Node.GetKeysCount(); + if (level[i].BeginRowId < row1) { + Y_DEBUG_ABORT_UNLESS(i == 0); + from = level[i].Node.Seek(row1); } - if (i + 1 == level.size() && row2 < meta.RowCount) { - to = level[i].Seek(row2); + if (level[i].EndRowId > row2) { + Y_DEBUG_ABORT_UNLESS(i == level.size() - 1); + to = level[i].Node.Seek(row2); } for (TRecIdx j : xrange(from, to + 1)) { - ready &= TryLoadNode(level[i].GetShortChild(j).PageId, nextLevel); + ready &= TryLoadNode(level[i], j, nextLevel); } } } @@ -73,16 +101,49 @@ class TChargeBTreeIndex : public ICharge { if (meta.LevelCount == 0) { ready &= HasDataPage(meta.PageId, { }); } else { + if (key1) { + for (auto it = level.begin(); it != level.end(); it++) { + if (!it->BeginKey.Count() || it->BeginKey.CompareTo(key1, &keyDefaults) <= 0) { + // Y_VERIFY_DEBUG_S(std::distance(level.begin(), it) < 1, "Should have key2 node at the begin"); + TRecIdx pos = it->Node.Seek(ESeek::Lower, key1, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); + + // shrink row1 to the first key >= key1 + row1 = Max(row1, pos + ? it->Node.GetShortChild(pos - 1).RowCount + : it->BeginRowId); + + // break; //??? + } + } + } + if (key2) { + for (auto it = level.rbegin(); it != level.rend(); it++) { + if (!it->EndKey.Count() || it->EndKey.CompareTo(key2, &keyDefaults) > 0) { + // Y_VERIFY_DEBUG_S(std::distance(level.rbegin(), it) < 2, "Should have key2 node at the end"); + TRecIdx pos = it->Node.Seek(ESeek::Lower, key2, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); + + // shrink row2 to the first key > key2 + row2 = Min(row2, it->Node.GetShortChild(pos).RowCount); + + // break; //??? + } + } + } + for (ui32 i : xrange(level.size())) { - TRecIdx from = 0, to = level[i].GetKeysCount(); - if (i == 0) { - from = level[i].Seek(row1); + if (level[i].EndRowId <= row1 || level[i].BeginRowId > row2) { + continue; // TODO: ??? + } + + TRecIdx from = 0, to = level[i].Node.GetKeysCount(); + if (level[i].BeginRowId < row1) { + from = level[i].Node.Seek(row1); } - if (i + 1 == level.size() && row2 < meta.RowCount) { - to = level[i].Seek(row2); + if (level[i].EndRowId > row2) { + to = level[i].Node.Seek(row2); } for (TRecIdx j : xrange(from, to + 1)) { - ready &= HasDataPage(level[i].GetShortChild(j).PageId, { }); + ready &= HasDataPage(level[i].Node.GetShortChild(j).PageId, { }); } } } @@ -91,7 +152,7 @@ class TChargeBTreeIndex : public ICharge { return {ready, false}; } - TResult DoReverse(const TCells key1, const TCells key2, TRowId row1, TRowId row2, + TResult DoReverse(TCells key1, TCells key2, TRowId row1, TRowId row2, const TKeyCellDefaults &keyDefaults, ui64 itemsLimit, ui64 bytesLimit) const noexcept override { bool ready = true; @@ -111,21 +172,23 @@ class TChargeBTreeIndex : public ICharge { } // level contains nodes in reverse order - TVector level, nextLevel(Reserve(2)); + TVector level, nextLevel(Reserve(3)); for (ui32 height = 0; height < meta.LevelCount && ready; height++) { if (height == 0) { - ready &= TryLoadNode(meta.PageId, nextLevel); + ready &= TryLoadRoot(meta, nextLevel); } else { for (ui32 i : xrange(level.size())) { - TRecIdx from = level[i].GetKeysCount(), to = 0; - if (i == 0) { - from = level[i].Seek(row1); + TRecIdx from = level[i].Node.GetKeysCount(), to = 0; + if (level[i].EndRowId > row1) { + Y_DEBUG_ABORT_UNLESS(i == 0); + from = level[i].Node.Seek(row1); } - if (i + 1 == level.size() && row2 < meta.RowCount) { - to = level[i].Seek(row2); + if (level[i].BeginRowId < row2) { + Y_DEBUG_ABORT_UNLESS(i == level.size() - 1); + to = level[i].Node.Seek(row2); } for (TRecIdx j = from + 1; j > to; j--) { - ready &= TryLoadNode(level[i].GetShortChild(j - 1).PageId, nextLevel); + ready &= TryLoadNode(level[i], j - 1, nextLevel); } } } @@ -143,15 +206,17 @@ class TChargeBTreeIndex : public ICharge { ready &= HasDataPage(meta.PageId, { }); } else { for (ui32 i : xrange(level.size())) { - TRecIdx from = level[i].GetKeysCount(), to = 0; - if (i == 0) { - from = level[i].Seek(row1); + TRecIdx from = level[i].Node.GetKeysCount(), to = 0; + if (level[i].EndRowId > row1) { + Y_DEBUG_ABORT_UNLESS(i == 0); + from = level[i].Node.Seek(row1); } - if (i + 1 == level.size() && row2 < meta.RowCount) { - to = level[i].Seek(row2); + if (level[i].BeginRowId < row2) { + Y_DEBUG_ABORT_UNLESS(i == level.size() - 1); + to = level[i].Node.Seek(row2); } for (TRecIdx j = from + 1; j > to; j--) { - ready &= HasDataPage(level[i].GetShortChild(j - 1).PageId, { }); + ready &= HasDataPage(level[i].Node.GetShortChild(j - 1).PageId, { }); } } } @@ -165,17 +230,55 @@ class TChargeBTreeIndex : public ICharge { return Env->TryGetPage(Part, pageId, groupId); } - bool TryLoadNode(TPageId pageId, TVector& level) const noexcept { - auto page = Env->TryGetPage(Part, pageId); - if (page) { - level.emplace_back(*page); - return true; + bool TryLoadRoot(const TBtreeIndexMeta& meta, TVector& level) const noexcept { + const static TCellsIterable EmptyKey = TCellsIterable(static_cast(nullptr), TColumns()); + + auto page = Env->TryGetPage(Part, meta.PageId); + if (!page) { + return false; + } + + level.emplace_back(meta, TBtreeIndexNode(*page), 0, meta.RowCount, EmptyKey, EmptyKey); + return true; + } + + bool TryLoadNode(TNodeState& current, TRecIdx pos, TVector& level) const noexcept { + Y_ABORT_UNLESS(pos < current.Node.GetChildrenCount(), "Should point to some child"); + + auto child = current.Node.GetChild(pos); + + auto page = Env->TryGetPage(Part, child.PageId); + if (!page) { + return false; } - return false; + + TRowId beginRowId = pos ? current.Node.GetChild(pos - 1).RowCount : current.BeginRowId; + TRowId endRowId = child.RowCount; + + TCellsIterable beginKey = pos ? current.Node.GetKeyCellsIterable(pos - 1, Scheme.Groups[0].ColsKeyIdx) : current.BeginKey; + TCellsIterable endKey = pos < current.Node.GetKeysCount() ? current.Node.GetKeyCellsIterable(pos, Scheme.Groups[0].ColsKeyIdx) : current.EndKey; + + level.emplace_back(child, TBtreeIndexNode(*page), beginRowId, endRowId, beginKey, endKey); + return true; + } + + int Compare(TCells left, TCells right, const TKeyCellDefaults &keyDefaults) const noexcept + { + for (TPos it = 0; it < Min(left.size(), right.size(), keyDefaults.Size()); it++) { + if (int cmp = CompareTypedCells(left[it], right[it], keyDefaults.Types[it])) { + return cmp; + } + } + + return left.size() == right.size() + ? 0 + // Missing point cells are filled with a virtual +inf + : (left.size() > right.size() ? -1 : 1); } private: const TPart* const Part; + const TPartScheme &Scheme; IPages* const Env; }; diff --git a/ydb/core/tablet_flat/flat_part_charge_range.cpp b/ydb/core/tablet_flat/flat_part_charge_range.cpp index 3ba7325fca91..99182670ea68 100644 --- a/ydb/core/tablet_flat/flat_part_charge_range.cpp +++ b/ydb/core/tablet_flat/flat_part_charge_range.cpp @@ -7,20 +7,14 @@ bool ChargeRange(IPages *env, const TCells key1, const TCells key2, const TRun &run, const TKeyCellDefaults &keyDefaults, TTagsRef tags, ui64 items, ui64 bytes, bool includeHistory) noexcept { - if (run.size() == 1) { - auto pos = run.begin(); - TRowId row1 = pos->Slice.BeginRowId(); - TRowId row2 = pos->Slice.EndRowId() - 1; - return CreateCharge(env, *pos->Part, tags, includeHistory)->Do(key1, key2, row1, row2, keyDefaults, items, bytes).Ready; - } - bool ready = true; auto pos = run.LowerBound(key1); if (pos == run.end()) return true; - bool fromStart = TSlice::CompareSearchKeyFirstKey(key1, pos->Slice, keyDefaults) <= 0; + // key1 <= FirstKey + bool seekToSliceFirstRow = TSlice::CompareSearchKeyFirstKey(key1, pos->Slice, keyDefaults) <= 0; while (pos != run.end()) { TRowId row1 = pos->Slice.BeginRowId(); @@ -29,20 +23,22 @@ bool ChargeRange(IPages *env, const TCells key1, const TCells key2, const int cmp = TSlice::CompareLastKeySearchKey(pos->Slice, key2, keyDefaults); TArrayRef key1r; - if (!fromStart) { + if (!seekToSliceFirstRow) { key1r = key1; } TArrayRef key2r; - if (cmp > 0 /* slice->LastKey > key2 */) { + if (cmp > 0) { + // key2 < LastKey key2r = key2; } auto r = CreateCharge(env, *pos->Part, tags, includeHistory)->Do(key1r, key2r, row1, row2, keyDefaults, items, bytes); ready &= r.Ready; - if (cmp >= 0 /* slice->LastKey >= key2 */) { + if (cmp >= 0) { + // key2 <= LastKey if (r.Overshot && ++pos != run.end()) { - // Unfortunately key > key2 might be at the start of the next slice + // Unfortunately first key >= key1 might be at the start of the next slice TRowId firstRow = pos->Slice.BeginRowId(); // Precharge the first row on the next slice CreateCharge(env, *pos->Part, tags, includeHistory)->Do(firstRow, firstRow, keyDefaults, items, bytes); @@ -52,7 +48,7 @@ bool ChargeRange(IPages *env, const TCells key1, const TCells key2, } // Will consume this slice before encountering key2 - fromStart = true; + seekToSliceFirstRow = true; ++pos; } @@ -63,20 +59,14 @@ bool ChargeRangeReverse(IPages *env, const TCells key1, const TCells key2, const TRun &run, const TKeyCellDefaults &keyDefaults, TTagsRef tags, ui64 items, ui64 bytes, bool includeHistory) noexcept { - if (run.size() == 1) { - auto pos = run.begin(); - TRowId row1 = pos->Slice.EndRowId() - 1; - TRowId row2 = pos->Slice.BeginRowId(); - return CreateCharge(env, *pos->Part, tags, includeHistory)->DoReverse(key1, key2, row1, row2, keyDefaults, items, bytes).Ready; - } - bool ready = true; auto pos = run.LowerBoundReverse(key1); if (pos == run.end()) return true; - bool fromEnd = TSlice::CompareLastKeySearchKey(pos->Slice, key1, keyDefaults) <= 0; + // LastKey <= key1 + bool seekToSliceLastRow = TSlice::CompareLastKeySearchKey(pos->Slice, key1, keyDefaults) <= 0; for (;;) { TRowId row1 = pos->Slice.EndRowId() - 1; @@ -86,11 +76,12 @@ bool ChargeRangeReverse(IPages *env, const TCells key1, const TCells key2, const int cmp = key2 ? TSlice::CompareSearchKeyFirstKey(key2, pos->Slice, keyDefaults) : -1; TArrayRef key1r; - if (!fromEnd) { + if (!seekToSliceLastRow) { key1r = key1; } TArrayRef key2r; - if (cmp > 0 /* key2 > slice->FirstKey */) { + if (cmp > 0) { + // FirstKey < key2 key2r = key2; } @@ -102,9 +93,10 @@ bool ChargeRangeReverse(IPages *env, const TCells key1, const TCells key2, } if (cmp >= 0 /* key2 >= slice->FirstKey */) { + // FirstKey <= key2 if (r.Overshot) { --pos; - // Unfortunately key <= key2 might be at the end of the previous slice + // Unfortunately first key <= key1 might be at the end of the previous slice TRowId lastRow = pos->Slice.EndRowId() - 1; // Precharge the last row on the previous slice CreateCharge(env, *pos->Part, tags, includeHistory)->DoReverse(lastRow, lastRow, keyDefaults, items, bytes); @@ -114,7 +106,7 @@ bool ChargeRangeReverse(IPages *env, const TCells key1, const TCells key2, } // Will consume this slice before encountering key2 - fromEnd = true; + seekToSliceLastRow = true; --pos; } 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 index 3c5acdb18eac..121276888244 100644 --- a/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp +++ b/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp @@ -2,6 +2,7 @@ #include "flat_part_btree_index_iter.h" #include "flat_part_charge.h" #include "flat_part_charge_btree_index.h" +#include "flat_part_charge_range.h" #include "flat_part_iter_multi.h" #include "test/libs/table/test_writer.h" #include @@ -444,8 +445,7 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { 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(), false); // CheckChargeKeys(part, tags, eggs.Scheme->Keys.Get(), true); // TODO: mixed } @@ -470,7 +470,7 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { 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) { + EReady Seek(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); @@ -482,7 +482,22 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { }, env, message, failsAllowed); } - void CheckIterateKey(const TPartEggs& eggs) { + void Charge(const TRun &run, const TVector tags, 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 + ? ChargeRangeReverse(&env, key1, key2, run, keyDefaults, tags, itemsLimit, bytesLimit, true) + : ChargeRange(&env, key1, key2, run, keyDefaults, tags, itemsLimit, bytesLimit, true); + if (result) { + return; + } + env.LoadTouched(true); + UNIT_ASSERT_C(failsAllowed--, "Too many fails " + message); + } + Y_UNREACHABLE(); + } + + void CheckIterate(const TPartEggs& eggs) { const auto part = *eggs.Lone(); TRun btreeRun(*eggs.Scheme->Keys), flatRun(*eggs.Scheme->Keys); @@ -512,18 +527,18 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { TRunIt bTree(btreeRun, tags, eggs.Scheme->Keys, &bTreeEnv); { - TStringBuilder message = TStringBuilder() << (reverse ? "SeekKeyReverse" : "SeekKey") << "(" << seek << ") "; + TStringBuilder message = TStringBuilder() << (reverse ? "IterateReverse" : "Iterate") << "(" << seek << ") "; for (auto c : key) { message << c.AsValue() << " "; } - EReady bTreeReady = SeekKey(bTree, bTreeEnv, seek, reverse, key, message); - EReady flatReady = SeekKey(flat, flatEnv, seek, reverse, key, message); + EReady bTreeReady = Seek(bTree, bTreeEnv, seek, reverse, key, message); + EReady flatReady = Seek(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 << ") "; + TStringBuilder message = TStringBuilder() << (reverse ? "IterateReverse" : "Iterate") << "(" << seek << ") "; for (auto c : key) { message << c.AsValue() << " "; } @@ -539,11 +554,63 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { } } + void CheckCharge(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*)&flatPart->IndexPages.BTreeGroups; + pages->clear(); + pages = (TVector*)&flatPart->IndexPages.BTreeHistoric; + pages->clear(); + flatRun.Insert(flatPart, slice); + } + + auto tags = TVector(); + for (auto c : eggs.Scheme->Cols) { + tags.push_back(c.Tag); + } + + for (bool reverse : {false, true}) { + for (ui32 firstCellKey1 : xrange(0, part.Stat.Rows / 7 + 1)) { + for (ui32 secondCellKey1 : xrange(0, 14)) { + for (ui32 firstCellKey2 : xrange(0, part.Stat.Rows / 7 + 1)) { + for (ui32 secondCellKey2 : xrange(0, 14)) { + TVector key1 = MakeKey(firstCellKey1, secondCellKey1); + TVector key2 = MakeKey(firstCellKey2, secondCellKey2); + + TTouchEnv bTreeEnv, flatEnv; + + TStringBuilder message = TStringBuilder() << (reverse ? "ChargeKeysReverse " : "ChargeKeys ") << "("; + for (auto c : key1) { + message << c.AsValue() << " "; + } + message << ") ("; + for (auto c : key2) { + message << c.AsValue() << " "; + } + message << ")"; + + // TODO: limits + Charge(btreeRun, tags, bTreeEnv, key1, key2, 0, 0, reverse, *eggs.Scheme->Keys, message); + Charge(flatRun, tags, flatEnv, key1, key2, 0, 0, reverse, *eggs.Scheme->Keys, message); + + AssertLoadTheSame(part, bTreeEnv, flatEnv, message); + } + } + } + } + } + } + void CheckPart(bool slices, ui32 levels) { TPartEggs eggs = MakePart(slices, levels); const auto part = *eggs.Lone(); - CheckIterateKey(eggs); + CheckIterate(eggs); + CheckCharge(eggs); } Y_UNIT_TEST(NoNodes_SingleSlice) { From 45f7a4f64529f6b7f0516c508c746b7f02c7e10f Mon Sep 17 00:00:00 2001 From: kungasc Date: Wed, 17 Jan 2024 14:15:27 +0000 Subject: [PATCH 07/20] revert comment --- ydb/core/tablet_flat/flat_part_charge_range.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ydb/core/tablet_flat/flat_part_charge_range.cpp b/ydb/core/tablet_flat/flat_part_charge_range.cpp index 99182670ea68..8c7b05a2e977 100644 --- a/ydb/core/tablet_flat/flat_part_charge_range.cpp +++ b/ydb/core/tablet_flat/flat_part_charge_range.cpp @@ -38,7 +38,7 @@ bool ChargeRange(IPages *env, const TCells key1, const TCells key2, if (cmp >= 0) { // key2 <= LastKey if (r.Overshot && ++pos != run.end()) { - // Unfortunately first key >= key1 might be at the start of the next slice + // Unfortunately first key > key2 might be at the start of the next slice TRowId firstRow = pos->Slice.BeginRowId(); // Precharge the first row on the next slice CreateCharge(env, *pos->Part, tags, includeHistory)->Do(firstRow, firstRow, keyDefaults, items, bytes); @@ -96,7 +96,7 @@ bool ChargeRangeReverse(IPages *env, const TCells key1, const TCells key2, // FirstKey <= key2 if (r.Overshot) { --pos; - // Unfortunately first key <= key1 might be at the end of the previous slice + // Unfortunately first key < key2 might be at the end of the previous slice TRowId lastRow = pos->Slice.EndRowId() - 1; // Precharge the last row on the previous slice CreateCharge(env, *pos->Part, tags, includeHistory)->DoReverse(lastRow, lastRow, keyDefaults, items, bytes); From f84adf3991cfcdb648becdc3fc7cb21358e37f06 Mon Sep 17 00:00:00 2001 From: kungasc Date: Wed, 17 Jan 2024 15:22:36 +0000 Subject: [PATCH 08/20] fix naming --- ydb/core/tablet_flat/flat_part_charge_range.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ydb/core/tablet_flat/flat_part_charge_range.cpp b/ydb/core/tablet_flat/flat_part_charge_range.cpp index 8c7b05a2e977..faf1643d4be1 100644 --- a/ydb/core/tablet_flat/flat_part_charge_range.cpp +++ b/ydb/core/tablet_flat/flat_part_charge_range.cpp @@ -14,7 +14,7 @@ bool ChargeRange(IPages *env, const TCells key1, const TCells key2, return true; // key1 <= FirstKey - bool seekToSliceFirstRow = TSlice::CompareSearchKeyFirstKey(key1, pos->Slice, keyDefaults) <= 0; + bool chargeFromSliceFirstRow = TSlice::CompareSearchKeyFirstKey(key1, pos->Slice, keyDefaults) <= 0; while (pos != run.end()) { TRowId row1 = pos->Slice.BeginRowId(); @@ -23,7 +23,7 @@ bool ChargeRange(IPages *env, const TCells key1, const TCells key2, const int cmp = TSlice::CompareLastKeySearchKey(pos->Slice, key2, keyDefaults); TArrayRef key1r; - if (!seekToSliceFirstRow) { + if (!chargeFromSliceFirstRow) { key1r = key1; } TArrayRef key2r; @@ -48,7 +48,7 @@ bool ChargeRange(IPages *env, const TCells key1, const TCells key2, } // Will consume this slice before encountering key2 - seekToSliceFirstRow = true; + chargeFromSliceFirstRow = true; ++pos; } @@ -66,7 +66,7 @@ bool ChargeRangeReverse(IPages *env, const TCells key1, const TCells key2, return true; // LastKey <= key1 - bool seekToSliceLastRow = TSlice::CompareLastKeySearchKey(pos->Slice, key1, keyDefaults) <= 0; + bool chargeFromSliceLastRow = TSlice::CompareLastKeySearchKey(pos->Slice, key1, keyDefaults) <= 0; for (;;) { TRowId row1 = pos->Slice.EndRowId() - 1; @@ -76,7 +76,7 @@ bool ChargeRangeReverse(IPages *env, const TCells key1, const TCells key2, const int cmp = key2 ? TSlice::CompareSearchKeyFirstKey(key2, pos->Slice, keyDefaults) : -1; TArrayRef key1r; - if (!seekToSliceLastRow) { + if (!chargeFromSliceLastRow) { key1r = key1; } TArrayRef key2r; @@ -106,7 +106,7 @@ bool ChargeRangeReverse(IPages *env, const TCells key1, const TCells key2, } // Will consume this slice before encountering key2 - seekToSliceLastRow = true; + chargeFromSliceLastRow = true; --pos; } From 61775051f82d43a0fc974cfb64274a422f14c578 Mon Sep 17 00:00:00 2001 From: kungasc Date: Thu, 18 Jan 2024 12:44:39 +0000 Subject: [PATCH 09/20] done forward keys charge --- .../flat_part_charge_btree_index.h | 129 +++++++----------- .../tablet_flat/test/libs/table/test_part.h | 2 +- .../ut/ut_btree_index_iter_charge.cpp | 16 +-- 3 files changed, 62 insertions(+), 85 deletions(-) diff --git a/ydb/core/tablet_flat/flat_part_charge_btree_index.h b/ydb/core/tablet_flat/flat_part_charge_btree_index.h index e07709f111d3..ec2d5b8f5bd3 100644 --- a/ydb/core/tablet_flat/flat_part_charge_btree_index.h +++ b/ydb/core/tablet_flat/flat_part_charge_btree_index.h @@ -20,16 +20,12 @@ class TChargeBTreeIndex : public ICharge { TBtreeIndexNode Node; TRowId BeginRowId; TRowId EndRowId; - TCellsIterable BeginKey; - TCellsIterable EndKey; - TNodeState(TChild meta, TBtreeIndexNode node, TRowId beginRowId, TRowId endRowId, TCellsIterable beginKey, TCellsIterable endKey) + TNodeState(TChild meta, TBtreeIndexNode node, TRowId beginRowId, TRowId endRowId) : Meta(meta) , Node(node) , BeginRowId(beginRowId) , EndRowId(endRowId) - , BeginKey(beginKey) - , EndKey(endKey) { } }; @@ -50,14 +46,16 @@ class TChargeBTreeIndex : public ICharge { const TKeyCellDefaults &keyDefaults, ui64 itemsLimit, ui64 bytesLimit) const noexcept override { bool ready = true; - Y_UNUSED(key1); - Y_UNUSED(key2); - Y_UNUSED(keyDefaults); Y_UNUSED(itemsLimit); Y_UNUSED(bytesLimit); const auto& meta = Part->IndexPages.BTreeGroups[0]; + if (meta.LevelCount == 0) { + ready &= HasDataPage(meta.PageId, { }); + return { ready, true }; + } + if (Y_UNLIKELY(row1 >= meta.RowCount)) { return { true, true }; // already out of bounds, nothing to precharge } @@ -68,71 +66,29 @@ class TChargeBTreeIndex : public ICharge { key2 = key1; // will not go further than key1 } - TVector level, nextLevel(Reserve(3)); - for (ui32 height = 0; height < meta.LevelCount && ready; height++) { - if (height == 0) { - ready &= TryLoadRoot(meta, nextLevel); - } else { - for (ui32 i : xrange(level.size())) { - TRecIdx from = 0, to = level[i].Node.GetKeysCount(); - if (level[i].BeginRowId < row1) { - Y_DEBUG_ABORT_UNLESS(i == 0); - from = level[i].Node.Seek(row1); - } - if (level[i].EndRowId > row2) { - Y_DEBUG_ABORT_UNLESS(i == level.size() - 1); - to = level[i].Node.Seek(row2); - } - for (TRecIdx j : xrange(from, to + 1)) { - ready &= TryLoadNode(level[i], j, nextLevel); - } - } - } - - level.swap(nextLevel); - nextLevel.clear(); - } + TVector level(Reserve(3)), nextLevel(Reserve(3)); + TPageId key1PageId = key1 ? meta.PageId : Max(); + TPageId key2PageId = key2 ? meta.PageId : Max(); - if (!ready) { - // some index pages are missing, do not continue - return {false, false}; - } - - if (meta.LevelCount == 0) { - ready &= HasDataPage(meta.PageId, { }); - } else { - if (key1) { - for (auto it = level.begin(); it != level.end(); it++) { - if (!it->BeginKey.Count() || it->BeginKey.CompareTo(key1, &keyDefaults) <= 0) { - // Y_VERIFY_DEBUG_S(std::distance(level.begin(), it) < 1, "Should have key2 node at the begin"); - TRecIdx pos = it->Node.Seek(ESeek::Lower, key1, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); - - // shrink row1 to the first key >= key1 - row1 = Max(row1, pos - ? it->Node.GetShortChild(pos - 1).RowCount - : it->BeginRowId); - - // break; //??? + const auto iterateLevel = [&](std::function tryLoadNext) { + for (ui32 i : xrange(level.size())) { + if (level[i].Meta.PageId == key1PageId) { + TRecIdx pos = level[i].Node.Seek(ESeek::Lower, key1, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); + key1PageId = level[i].Node.GetShortChild(pos).PageId; + if (pos) { + // move row1 to the first key >= key1 + row1 = Max(row1, level[i].Node.GetShortChild(pos - 1).RowCount); } } - } - if (key2) { - for (auto it = level.rbegin(); it != level.rend(); it++) { - if (!it->EndKey.Count() || it->EndKey.CompareTo(key2, &keyDefaults) > 0) { - // Y_VERIFY_DEBUG_S(std::distance(level.rbegin(), it) < 2, "Should have key2 node at the end"); - TRecIdx pos = it->Node.Seek(ESeek::Lower, key2, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); - - // shrink row2 to the first key > key2 - row2 = Min(row2, it->Node.GetShortChild(pos).RowCount); - - // break; //??? - } + if (level[i].Meta.PageId == key2PageId) { + TRecIdx pos = level[i].Node.Seek(ESeek::Lower, key2, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); + key2PageId = level[i].Node.GetShortChild(pos).PageId; + // move row2 to the first key > key2 + row2 = Min(row2, level[i].Node.GetShortChild(pos).RowCount); } - } - - for (ui32 i : xrange(level.size())) { + if (level[i].EndRowId <= row1 || level[i].BeginRowId > row2) { - continue; // TODO: ??? + continue; } TRecIdx from = 0, to = level[i].Node.GetKeysCount(); @@ -143,11 +99,34 @@ class TChargeBTreeIndex : public ICharge { to = level[i].Node.Seek(row2); } for (TRecIdx j : xrange(from, to + 1)) { - ready &= HasDataPage(level[i].Node.GetShortChild(j).PageId, { }); + ready &= tryLoadNext(level[i], j); } } + }; + + const auto tryLoadNode = [&](TNodeState& current, TRecIdx pos) -> bool { + return TryLoadNode(current, pos, nextLevel); + }; + + const auto hasDataPage = [&](TNodeState& current, TRecIdx pos) -> bool { + return HasDataPage(current.Node.GetShortChild(pos).PageId, { }); + }; + + ready &= TryLoadRoot(meta, level); + + for (ui32 height = 1; height < meta.LevelCount && ready; height++) { + iterateLevel(tryLoadNode); + level.swap(nextLevel); + nextLevel.clear(); } + if (!ready) { + // some index pages are missing, do not continue + return {false, false}; + } + + iterateLevel(hasDataPage); + // TODO: overshot for keys search return {ready, false}; } @@ -231,14 +210,12 @@ class TChargeBTreeIndex : public ICharge { } bool TryLoadRoot(const TBtreeIndexMeta& meta, TVector& level) const noexcept { - const static TCellsIterable EmptyKey = TCellsIterable(static_cast(nullptr), TColumns()); - auto page = Env->TryGetPage(Part, meta.PageId); if (!page) { return false; } - level.emplace_back(meta, TBtreeIndexNode(*page), 0, meta.RowCount, EmptyKey, EmptyKey); + level.emplace_back(meta, TBtreeIndexNode(*page), 0, meta.RowCount); return true; } @@ -255,15 +232,15 @@ class TChargeBTreeIndex : public ICharge { TRowId beginRowId = pos ? current.Node.GetChild(pos - 1).RowCount : current.BeginRowId; TRowId endRowId = child.RowCount; - TCellsIterable beginKey = pos ? current.Node.GetKeyCellsIterable(pos - 1, Scheme.Groups[0].ColsKeyIdx) : current.BeginKey; - TCellsIterable endKey = pos < current.Node.GetKeysCount() ? current.Node.GetKeyCellsIterable(pos, Scheme.Groups[0].ColsKeyIdx) : current.EndKey; - - level.emplace_back(child, TBtreeIndexNode(*page), beginRowId, endRowId, beginKey, endKey); + level.emplace_back(child, TBtreeIndexNode(*page), beginRowId, endRowId); return true; } int Compare(TCells left, TCells right, const TKeyCellDefaults &keyDefaults) const noexcept { + Y_DEBUG_ABORT_UNLESS(left, "Empty keys should be handled separately"); + Y_DEBUG_ABORT_UNLESS(right, "Empty keys should be handled separately"); + for (TPos it = 0; it < Min(left.size(), right.size(), keyDefaults.Size()); it++) { if (int cmp = CompareTypedCells(left[it], right[it], keyDefaults.Types[it])) { return cmp; 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 f67cb654caf5..b627564ca00b 100644 --- a/ydb/core/tablet_flat/test/libs/table/test_part.h +++ b/ydb/core/tablet_flat/test/libs/table/test_part.h @@ -176,7 +176,7 @@ namespace NTest { return index.GetLastRecord(); } - inline const TPartIndexIt::TRecord * GetRecord(const TPartStore& part, TPageId pageIndex) { + inline const TPartIndexIt::TRecord * GetRecord(const TPartStore& part, ui32 pageIndex) { TTestEnv env; TPartIndexIt index(&part, &env, { }); 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 index 121276888244..094684c7a94f 100644 --- a/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp +++ b/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp @@ -39,7 +39,7 @@ namespace { TMap> Touched; }; - void AssertLoadTheSame(const TPartStore& part, const TTouchEnv& bTree, const TTouchEnv& flat, const TString& message) { + void AssertLoadedTheSame(const TPartStore& part, const TTouchEnv& bTree, const TTouchEnv& flat, const TString& message) { TSet groupIds; for (const auto &c : {bTree.Loaded, flat.Loaded}) { for (const auto &g : c) { @@ -112,7 +112,7 @@ namespace { } return TSerializedCellVec(key); }; - auto add = [&](TRowId pageIndex1 /*inclusive*/, TRowId pageIndex2 /*exclusive*/) { + auto add = [&](ui32 pageIndex1 /*inclusive*/, ui32 pageIndex2 /*exclusive*/) { TSlice slice; slice.FirstInclusive = true; slice.FirstRowId = pageIndex1 * 2; @@ -392,7 +392,7 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { 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); + AssertLoadedTheSame(part, bTreeEnv, flatEnv, message); } } } @@ -427,7 +427,7 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { Y_UNUSED(bTreeOvershot); Y_UNUSED(flatOvershot); - AssertLoadTheSame(part, bTreeEnv, flatEnv, message); + AssertLoadedTheSame(part, bTreeEnv, flatEnv, message); } } } @@ -534,7 +534,7 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { EReady bTreeReady = Seek(bTree, bTreeEnv, seek, reverse, key, message); EReady flatReady = Seek(flat, flatEnv, seek, reverse, key, message); AssertEqual(bTree, bTreeReady, flat, flatReady, message); - AssertLoadTheSame(part, bTreeEnv, flatEnv, message); + AssertLoadedTheSame(part, bTreeEnv, flatEnv, message); } for (ui32 steps = 1; steps <= 10; steps++) { @@ -546,7 +546,7 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { 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); + AssertLoadedTheSame(part, bTreeEnv, flatEnv, message); } } } @@ -583,7 +583,7 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { TTouchEnv bTreeEnv, flatEnv; - TStringBuilder message = TStringBuilder() << (reverse ? "ChargeKeysReverse " : "ChargeKeys ") << "("; + TStringBuilder message = TStringBuilder() << (reverse ? "ChargeReverse " : "Charge ") << "("; for (auto c : key1) { message << c.AsValue() << " "; } @@ -597,7 +597,7 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) { Charge(btreeRun, tags, bTreeEnv, key1, key2, 0, 0, reverse, *eggs.Scheme->Keys, message); Charge(flatRun, tags, flatEnv, key1, key2, 0, 0, reverse, *eggs.Scheme->Keys, message); - AssertLoadTheSame(part, bTreeEnv, flatEnv, message); + AssertLoadedTheSame(part, bTreeEnv, flatEnv, message); } } } From f65077ae209330217a27122077b8a38c85a8ea65 Mon Sep 17 00:00:00 2001 From: kungasc Date: Thu, 18 Jan 2024 13:42:56 +0000 Subject: [PATCH 10/20] use inheritance --- .../flat_part_charge_btree_index.h | 62 +++++++++---------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/ydb/core/tablet_flat/flat_part_charge_btree_index.h b/ydb/core/tablet_flat/flat_part_charge_btree_index.h index ec2d5b8f5bd3..b0f2f61afe7a 100644 --- a/ydb/core/tablet_flat/flat_part_charge_btree_index.h +++ b/ydb/core/tablet_flat/flat_part_charge_btree_index.h @@ -12,18 +12,15 @@ class TChargeBTreeIndex : public ICharge { using TRecIdx = NPage::TRecIdx; using TGroupId = NPage::TGroupId; using TChild = TBtreeIndexNode::TChild; - using TColumns = TBtreeIndexNode::TColumns; - using TCellsIterable = TBtreeIndexNode::TCellsIterable; - struct TNodeState { - TChild Meta; - TBtreeIndexNode Node; + // TODO: store PageId only instead of TChild? + struct TNodeState : TBtreeIndexNode, TChild { TRowId BeginRowId; TRowId EndRowId; - TNodeState(TChild meta, TBtreeIndexNode node, TRowId beginRowId, TRowId endRowId) - : Meta(meta) - , Node(node) + TNodeState(TSharedData data, TChild meta, TRowId beginRowId, TRowId endRowId) + : TBtreeIndexNode(data) + , TChild(meta) , BeginRowId(beginRowId) , EndRowId(endRowId) { @@ -35,7 +32,6 @@ class TChargeBTreeIndex : public ICharge { : Part(&part) , Scheme(*Part->Scheme) , Env(env) { - Y_UNUSED(env); Y_UNUSED(part); Y_UNUSED(tags); Y_UNUSED(includeHistory); @@ -72,31 +68,31 @@ class TChargeBTreeIndex : public ICharge { const auto iterateLevel = [&](std::function tryLoadNext) { for (ui32 i : xrange(level.size())) { - if (level[i].Meta.PageId == key1PageId) { - TRecIdx pos = level[i].Node.Seek(ESeek::Lower, key1, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); - key1PageId = level[i].Node.GetShortChild(pos).PageId; + if (level[i].PageId == key1PageId) { + TRecIdx pos = level[i].Seek(ESeek::Lower, key1, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); + key1PageId = level[i].GetShortChild(pos).PageId; if (pos) { // move row1 to the first key >= key1 - row1 = Max(row1, level[i].Node.GetShortChild(pos - 1).RowCount); + row1 = Max(row1, level[i].GetShortChild(pos - 1).RowCount); } } - if (level[i].Meta.PageId == key2PageId) { - TRecIdx pos = level[i].Node.Seek(ESeek::Lower, key2, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); - key2PageId = level[i].Node.GetShortChild(pos).PageId; + if (level[i].PageId == key2PageId) { + TRecIdx pos = level[i].Seek(ESeek::Lower, key2, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); + key2PageId = level[i].GetShortChild(pos).PageId; // move row2 to the first key > key2 - row2 = Min(row2, level[i].Node.GetShortChild(pos).RowCount); + row2 = Min(row2, level[i].GetShortChild(pos).RowCount); } if (level[i].EndRowId <= row1 || level[i].BeginRowId > row2) { continue; } - TRecIdx from = 0, to = level[i].Node.GetKeysCount(); + TRecIdx from = 0, to = level[i].GetKeysCount(); if (level[i].BeginRowId < row1) { - from = level[i].Node.Seek(row1); + from = level[i].Seek(row1); } if (level[i].EndRowId > row2) { - to = level[i].Node.Seek(row2); + to = level[i].Seek(row2); } for (TRecIdx j : xrange(from, to + 1)) { ready &= tryLoadNext(level[i], j); @@ -109,7 +105,7 @@ class TChargeBTreeIndex : public ICharge { }; const auto hasDataPage = [&](TNodeState& current, TRecIdx pos) -> bool { - return HasDataPage(current.Node.GetShortChild(pos).PageId, { }); + return HasDataPage(current.GetShortChild(pos).PageId, { }); }; ready &= TryLoadRoot(meta, level); @@ -157,14 +153,14 @@ class TChargeBTreeIndex : public ICharge { ready &= TryLoadRoot(meta, nextLevel); } else { for (ui32 i : xrange(level.size())) { - TRecIdx from = level[i].Node.GetKeysCount(), to = 0; + TRecIdx from = level[i].GetKeysCount(), to = 0; if (level[i].EndRowId > row1) { Y_DEBUG_ABORT_UNLESS(i == 0); - from = level[i].Node.Seek(row1); + from = level[i].Seek(row1); } if (level[i].BeginRowId < row2) { Y_DEBUG_ABORT_UNLESS(i == level.size() - 1); - to = level[i].Node.Seek(row2); + to = level[i].Seek(row2); } for (TRecIdx j = from + 1; j > to; j--) { ready &= TryLoadNode(level[i], j - 1, nextLevel); @@ -185,17 +181,17 @@ class TChargeBTreeIndex : public ICharge { ready &= HasDataPage(meta.PageId, { }); } else { for (ui32 i : xrange(level.size())) { - TRecIdx from = level[i].Node.GetKeysCount(), to = 0; + TRecIdx from = level[i].GetKeysCount(), to = 0; if (level[i].EndRowId > row1) { Y_DEBUG_ABORT_UNLESS(i == 0); - from = level[i].Node.Seek(row1); + from = level[i].Seek(row1); } if (level[i].BeginRowId < row2) { Y_DEBUG_ABORT_UNLESS(i == level.size() - 1); - to = level[i].Node.Seek(row2); + to = level[i].Seek(row2); } for (TRecIdx j = from + 1; j > to; j--) { - ready &= HasDataPage(level[i].Node.GetShortChild(j - 1).PageId, { }); + ready &= HasDataPage(level[i].GetShortChild(j - 1).PageId, { }); } } } @@ -215,24 +211,24 @@ class TChargeBTreeIndex : public ICharge { return false; } - level.emplace_back(meta, TBtreeIndexNode(*page), 0, meta.RowCount); + level.emplace_back(*page, meta, 0, meta.RowCount); return true; } bool TryLoadNode(TNodeState& current, TRecIdx pos, TVector& level) const noexcept { - Y_ABORT_UNLESS(pos < current.Node.GetChildrenCount(), "Should point to some child"); + Y_ABORT_UNLESS(pos < current.GetChildrenCount(), "Should point to some child"); - auto child = current.Node.GetChild(pos); + auto child = current.GetChild(pos); auto page = Env->TryGetPage(Part, child.PageId); if (!page) { return false; } - TRowId beginRowId = pos ? current.Node.GetChild(pos - 1).RowCount : current.BeginRowId; + TRowId beginRowId = pos ? current.GetChild(pos - 1).RowCount : current.BeginRowId; TRowId endRowId = child.RowCount; - level.emplace_back(child, TBtreeIndexNode(*page), beginRowId, endRowId); + level.emplace_back(*page, child, beginRowId, endRowId); return true; } From 5312bf2112e1b7f1ee49755e8dcb48fec909e7d3 Mon Sep 17 00:00:00 2001 From: kungasc Date: Thu, 18 Jan 2024 14:45:40 +0000 Subject: [PATCH 11/20] reverse --- .../flat_part_charge_btree_index.h | 95 +++++++++++-------- .../tablet_flat/flat_part_charge_range.cpp | 2 +- 2 files changed, 57 insertions(+), 40 deletions(-) diff --git a/ydb/core/tablet_flat/flat_part_charge_btree_index.h b/ydb/core/tablet_flat/flat_part_charge_btree_index.h index b0f2f61afe7a..0f4385189a8c 100644 --- a/ydb/core/tablet_flat/flat_part_charge_btree_index.h +++ b/ydb/core/tablet_flat/flat_part_charge_btree_index.h @@ -91,7 +91,7 @@ class TChargeBTreeIndex : public ICharge { if (level[i].BeginRowId < row1) { from = level[i].Seek(row1); } - if (level[i].EndRowId > row2) { + if (level[i].EndRowId > row2 + 1) { to = level[i].Seek(row2); } for (TRecIdx j : xrange(from, to + 1)) { @@ -131,71 +131,88 @@ class TChargeBTreeIndex : public ICharge { const TKeyCellDefaults &keyDefaults, ui64 itemsLimit, ui64 bytesLimit) const noexcept override { bool ready = true; - Y_UNUSED(key1); - Y_UNUSED(key2); - Y_UNUSED(keyDefaults); Y_UNUSED(itemsLimit); Y_UNUSED(bytesLimit); const auto& meta = Part->IndexPages.BTreeGroups[0]; + if (meta.LevelCount == 0) { + ready &= HasDataPage(meta.PageId, { }); + return { ready, true }; + } + if (Y_UNLIKELY(row1 >= meta.RowCount)) { row1 = meta.RowCount - 1; // start from the last row } if (Y_UNLIKELY(row1 < row2)) { row2 = row1; // will not go further than row1 } + if (Y_UNLIKELY(key1 && key2 && Compare(key2, key1, keyDefaults) > 0)) { + key2 = key1; // will not go further than key1 + } // level contains nodes in reverse order - TVector level, nextLevel(Reserve(3)); - for (ui32 height = 0; height < meta.LevelCount && ready; height++) { - if (height == 0) { - ready &= TryLoadRoot(meta, nextLevel); - } else { - for (ui32 i : xrange(level.size())) { - TRecIdx from = level[i].GetKeysCount(), to = 0; - if (level[i].EndRowId > row1) { - Y_DEBUG_ABORT_UNLESS(i == 0); - from = level[i].Seek(row1); - } - if (level[i].BeginRowId < row2) { - Y_DEBUG_ABORT_UNLESS(i == level.size() - 1); - to = level[i].Seek(row2); - } - for (TRecIdx j = from + 1; j > to; j--) { - ready &= TryLoadNode(level[i], j - 1, nextLevel); + TVector level(Reserve(3)), nextLevel(Reserve(3)); + TPageId key1PageId = key1 ? meta.PageId : Max(); + TPageId key2PageId = key2 ? meta.PageId : Max(); + + const auto iterateLevel = [&](std::function tryLoadNext) { + for (ui32 i : xrange(level.size())) { + if (level[i].PageId == key1PageId) { + TRecIdx pos = level[i].SeekReverse(ESeek::Lower, key1, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); + key1PageId = level[i].GetShortChild(pos).PageId; + // move row1 to the first key <= key1 + row1 = Min(row1, level[i].GetShortChild(pos).RowCount - 1); + } + if (level[i].PageId == key2PageId) { + TRecIdx pos = level[i].SeekReverse(ESeek::Lower, key2, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); + key2PageId = level[i].GetShortChild(pos).PageId; + // move row2 to the first key > key2 + if (pos) { + row2 = Max(row2, level[i].GetShortChild(pos - 1).RowCount - 1); } } - } - - level.swap(nextLevel); - nextLevel.clear(); - } - - if (!ready) { - // some index pages are missing, do not continue - return {false, false}; - } + + if (level[i].EndRowId <= row2 || level[i].BeginRowId > row1) { + continue; + } - if (meta.LevelCount == 0) { - ready &= HasDataPage(meta.PageId, { }); - } else { - for (ui32 i : xrange(level.size())) { TRecIdx from = level[i].GetKeysCount(), to = 0; - if (level[i].EndRowId > row1) { - Y_DEBUG_ABORT_UNLESS(i == 0); + if (level[i].EndRowId > row1 + 1) { from = level[i].Seek(row1); } if (level[i].BeginRowId < row2) { - Y_DEBUG_ABORT_UNLESS(i == level.size() - 1); to = level[i].Seek(row2); } for (TRecIdx j = from + 1; j > to; j--) { - ready &= HasDataPage(level[i].GetShortChild(j - 1).PageId, { }); + ready &= tryLoadNext(level[i], j - 1); } } + }; + + const auto tryLoadNode = [&](TNodeState& current, TRecIdx pos) -> bool { + return TryLoadNode(current, pos, nextLevel); + }; + + const auto hasDataPage = [&](TNodeState& current, TRecIdx pos) -> bool { + return HasDataPage(current.GetShortChild(pos).PageId, { }); + }; + + ready &= TryLoadRoot(meta, level); + + for (ui32 height = 1; height < meta.LevelCount && ready; height++) { + iterateLevel(tryLoadNode); + level.swap(nextLevel); + nextLevel.clear(); } + if (!ready) { + // some index pages are missing, do not continue + return {false, false}; + } + + iterateLevel(hasDataPage); + // TODO: overshot for keys search return {ready, false}; } diff --git a/ydb/core/tablet_flat/flat_part_charge_range.cpp b/ydb/core/tablet_flat/flat_part_charge_range.cpp index faf1643d4be1..ad6bc2819f3f 100644 --- a/ydb/core/tablet_flat/flat_part_charge_range.cpp +++ b/ydb/core/tablet_flat/flat_part_charge_range.cpp @@ -96,7 +96,7 @@ bool ChargeRangeReverse(IPages *env, const TCells key1, const TCells key2, // FirstKey <= key2 if (r.Overshot) { --pos; - // Unfortunately first key < key2 might be at the end of the previous slice + // Unfortunately first key <= key2 might be at the end of the previous slice TRowId lastRow = pos->Slice.EndRowId() - 1; // Precharge the last row on the previous slice CreateCharge(env, *pos->Part, tags, includeHistory)->DoReverse(lastRow, lastRow, keyDefaults, items, bytes); From ad597ffad635d0da2ab8001071268411ddca5eed Mon Sep 17 00:00:00 2001 From: kungasc Date: Thu, 18 Jan 2024 17:36:07 +0000 Subject: [PATCH 12/20] overshot --- .../tablet_flat/flat_part_charge_btree_index.h | 15 ++++++++++----- ydb/core/tablet_flat/flat_part_charge_range.cpp | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/ydb/core/tablet_flat/flat_part_charge_btree_index.h b/ydb/core/tablet_flat/flat_part_charge_btree_index.h index 0f4385189a8c..9203b09a34a8 100644 --- a/ydb/core/tablet_flat/flat_part_charge_btree_index.h +++ b/ydb/core/tablet_flat/flat_part_charge_btree_index.h @@ -58,6 +58,7 @@ class TChargeBTreeIndex : public ICharge { if (Y_UNLIKELY(row1 > row2)) { row2 = row1; // will not go further than row1 } + TRowId sliceRow2 = row2; if (Y_UNLIKELY(key1 && key2 && Compare(key1, key2, keyDefaults) > 0)) { key2 = key1; // will not go further than key1 } @@ -81,6 +82,8 @@ class TChargeBTreeIndex : public ICharge { key2PageId = level[i].GetShortChild(pos).PageId; // move row2 to the first key > key2 row2 = Min(row2, level[i].GetShortChild(pos).RowCount); + // always charge row2, no matter what keys are + row2 = Max(row2, row1); } if (level[i].EndRowId <= row1 || level[i].BeginRowId > row2) { @@ -105,7 +108,7 @@ class TChargeBTreeIndex : public ICharge { }; const auto hasDataPage = [&](TNodeState& current, TRecIdx pos) -> bool { - return HasDataPage(current.GetShortChild(pos).PageId, { }); + return HasDataPage(current.GetShortChild(pos).PageId, { }); }; ready &= TryLoadRoot(meta, level); @@ -123,8 +126,7 @@ class TChargeBTreeIndex : public ICharge { iterateLevel(hasDataPage); - // TODO: overshot for keys search - return {ready, false}; + return {ready, row2 == sliceRow2}; } TResult DoReverse(TCells key1, TCells key2, TRowId row1, TRowId row2, @@ -144,9 +146,10 @@ class TChargeBTreeIndex : public ICharge { if (Y_UNLIKELY(row1 >= meta.RowCount)) { row1 = meta.RowCount - 1; // start from the last row } - if (Y_UNLIKELY(row1 < row2)) { + if (Y_UNLIKELY(row2 > row1)) { row2 = row1; // will not go further than row1 } + TRowId sliceRow2 = row2; if (Y_UNLIKELY(key1 && key2 && Compare(key2, key1, keyDefaults) > 0)) { key2 = key1; // will not go further than key1 } @@ -170,6 +173,8 @@ class TChargeBTreeIndex : public ICharge { // move row2 to the first key > key2 if (pos) { row2 = Max(row2, level[i].GetShortChild(pos - 1).RowCount - 1); + // always charge row1, no matter what keys are + row2 = Min(row2, row1); } } @@ -214,7 +219,7 @@ class TChargeBTreeIndex : public ICharge { iterateLevel(hasDataPage); // TODO: overshot for keys search - return {ready, false}; + return {ready, row2 == sliceRow2}; } private: diff --git a/ydb/core/tablet_flat/flat_part_charge_range.cpp b/ydb/core/tablet_flat/flat_part_charge_range.cpp index ad6bc2819f3f..2906bcd8abe0 100644 --- a/ydb/core/tablet_flat/flat_part_charge_range.cpp +++ b/ydb/core/tablet_flat/flat_part_charge_range.cpp @@ -41,7 +41,7 @@ bool ChargeRange(IPages *env, const TCells key1, const TCells key2, // Unfortunately first key > key2 might be at the start of the next slice TRowId firstRow = pos->Slice.BeginRowId(); // Precharge the first row on the next slice - CreateCharge(env, *pos->Part, tags, includeHistory)->Do(firstRow, firstRow, keyDefaults, items, bytes); + ready &= CreateCharge(env, *pos->Part, tags, includeHistory)->Do(firstRow, firstRow, keyDefaults, items, bytes); } break; @@ -99,7 +99,7 @@ bool ChargeRangeReverse(IPages *env, const TCells key1, const TCells key2, // Unfortunately first key <= key2 might be at the end of the previous slice TRowId lastRow = pos->Slice.EndRowId() - 1; // Precharge the last row on the previous slice - CreateCharge(env, *pos->Part, tags, includeHistory)->DoReverse(lastRow, lastRow, keyDefaults, items, bytes); + ready &= CreateCharge(env, *pos->Part, tags, includeHistory)->DoReverse(lastRow, lastRow, keyDefaults, items, bytes); } break; From 105de3819040c2a650c835b9ec0d808e1b96a006 Mon Sep 17 00:00:00 2001 From: kungasc Date: Fri, 19 Jan 2024 09:57:56 +0000 Subject: [PATCH 13/20] allow borders diff --- .../tablet_flat/test/libs/table/test_part.h | 14 +++++++ .../ut/ut_btree_index_iter_charge.cpp | 39 +++++++++++-------- 2 files changed, 36 insertions(+), 17 deletions(-) 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 b627564ca00b..024715575350 100644 --- a/ydb/core/tablet_flat/test/libs/table/test_part.h +++ b/ydb/core/tablet_flat/test/libs/table/test_part.h @@ -187,6 +187,20 @@ namespace NTest { return index.GetRecord(); } + + inline TPageId GetFirstPageId(const TPartStore& part) { + TTestEnv env; + TPartIndexIt index(&part, &env, { }); + index.Seek(0); + return index.GetPageId(); + } + + inline TPageId GetLastPageId(const TPartStore& part) { + TTestEnv env; + TPartIndexIt index(&part, &env, { }); + index.Seek(index.GetEndRowId() - 1); + return index.GetPageId(); + } } }}} 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 index 094684c7a94f..86f7a71be77c 100644 --- a/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp +++ b/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp @@ -39,7 +39,7 @@ namespace { TMap> Touched; }; - void AssertLoadedTheSame(const TPartStore& part, const TTouchEnv& bTree, const TTouchEnv& flat, const TString& message) { + void AssertLoadedTheSame(const TPartStore& part, const TTouchEnv& bTree, const TTouchEnv& flat, const TString& message, bool allowFirstLastPageDifference = false) { TSet groupIds; for (const auto &c : {bTree.Loaded, flat.Loaded}) { for (const auto &g : c) { @@ -60,7 +60,17 @@ namespace { } } - UNIT_ASSERT_VALUES_EQUAL_C(flatDataPages, bTreeDataPages, message); + // Note: it's possible that B-Tree index touches extra first / last page because it doesn't have boundary keys + // this should be resolved using slices (see ChargeRange) + if (allowFirstLastPageDifference) { + for (auto additionalPageId : {IndexTools::GetFirstPageId(part), IndexTools::GetLastPageId(part)}) { + if (bTreeDataPages.contains(additionalPageId)) { + flatDataPages.insert(additionalPageId); + } + } + } else { + UNIT_ASSERT_VALUES_EQUAL_C(flatDataPages, bTreeDataPages, message); + } } } @@ -367,12 +377,12 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { Y_UNREACHABLE(); } - bool DoChargeKeys(ICharge& charge, TTouchEnv& env, const TCells key1, const TCells key2, ui64 itemsLimit, ui64 bytesLimit, + bool DoChargeKeys(const TPartStore& part, 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(), keyDefaults, itemsLimit, bytesLimit) - : charge.Do(key1, key2, 0, Max(), keyDefaults, itemsLimit, bytesLimit); + ? charge.DoReverse(key1, key2, 0, part.Stat.Rows - 1, keyDefaults, itemsLimit, bytesLimit) + : charge.Do(key1, key2, 0, part.Stat.Rows - 1, keyDefaults, itemsLimit, bytesLimit); if (result.Ready) { return result.Overshot; } @@ -383,8 +393,8 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { } 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)) { + for (TRowId rowId1 : xrange(part.Stat.Rows)) { + for (TRowId rowId2 : xrange(part.Stat.Rows)) { TTouchEnv bTreeEnv, flatEnv; TChargeBTreeIndex bTree(&bTreeEnv, part, tags, true); TCharge flat(&flatEnv, part, tags, true); @@ -419,15 +429,11 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { } 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); + bool bTreeOvershot = DoChargeKeys(part, bTree, bTreeEnv, key1, key2, 0, 0, reverse, *keyDefaults, message); + bool flatOvershot = DoChargeKeys(part, flat, flatEnv, key1, key2, 0, 0, reverse, *keyDefaults, message); - // TODO - // UNIT_ASSERT_VALUES_EQUAL_C(bTreeOvershot, flatOvershot, message); - Y_UNUSED(bTreeOvershot); - Y_UNUSED(flatOvershot); - - AssertLoadedTheSame(part, bTreeEnv, flatEnv, message); + UNIT_ASSERT_VALUES_EQUAL_C(bTreeOvershot, flatOvershot, message); + AssertLoadedTheSame(part, bTreeEnv, flatEnv, message, true); } } } @@ -446,8 +452,7 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { CheckChargeRowId(part, tags, eggs.Scheme->Keys.Get(), false); CheckChargeRowId(part, tags, eggs.Scheme->Keys.Get(), true); CheckChargeKeys(part, tags, eggs.Scheme->Keys.Get(), false); - // CheckChargeKeys(part, tags, eggs.Scheme->Keys.Get(), true); - // TODO: mixed + CheckChargeKeys(part, tags, eggs.Scheme->Keys.Get(), true); } Y_UNIT_TEST(NoNodes) { From 51c5e095577d0497a8c48782676b7f571f7ab8ae Mon Sep 17 00:00:00 2001 From: kungasc Date: Fri, 19 Jan 2024 10:36:31 +0000 Subject: [PATCH 14/20] fix reverse call --- ydb/core/tablet_flat/flat_part_charge_btree_index.h | 5 ++--- ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ydb/core/tablet_flat/flat_part_charge_btree_index.h b/ydb/core/tablet_flat/flat_part_charge_btree_index.h index 9203b09a34a8..3796afdc0415 100644 --- a/ydb/core/tablet_flat/flat_part_charge_btree_index.h +++ b/ydb/core/tablet_flat/flat_part_charge_btree_index.h @@ -166,14 +166,14 @@ class TChargeBTreeIndex : public ICharge { key1PageId = level[i].GetShortChild(pos).PageId; // move row1 to the first key <= key1 row1 = Min(row1, level[i].GetShortChild(pos).RowCount - 1); - } + } if (level[i].PageId == key2PageId) { TRecIdx pos = level[i].SeekReverse(ESeek::Lower, key2, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); key2PageId = level[i].GetShortChild(pos).PageId; // move row2 to the first key > key2 if (pos) { row2 = Max(row2, level[i].GetShortChild(pos - 1).RowCount - 1); - // always charge row1, no matter what keys are + // always charge row1, no matter what keys are row2 = Min(row2, row1); } } @@ -218,7 +218,6 @@ class TChargeBTreeIndex : public ICharge { iterateLevel(hasDataPage); - // TODO: overshot for keys search return {ready, row2 == sliceRow2}; } 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 index 86f7a71be77c..be356402fe95 100644 --- a/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp +++ b/ydb/core/tablet_flat/ut/ut_btree_index_iter_charge.cpp @@ -381,7 +381,7 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) { bool reverse, const TKeyCellDefaults &keyDefaults, const TString& message, ui32 failsAllowed = 10) { while (true) { auto result = reverse - ? charge.DoReverse(key1, key2, 0, part.Stat.Rows - 1, keyDefaults, itemsLimit, bytesLimit) + ? charge.DoReverse(key1, key2, part.Stat.Rows - 1, 0, keyDefaults, itemsLimit, bytesLimit) : charge.Do(key1, key2, 0, part.Stat.Rows - 1, keyDefaults, itemsLimit, bytesLimit); if (result.Ready) { return result.Overshot; From 3d5daea059e0438653bade4d0a269e29e212c927 Mon Sep 17 00:00:00 2001 From: kungasc Date: Fri, 19 Jan 2024 11:59:48 +0000 Subject: [PATCH 15/20] fix tab --- ydb/core/tablet_flat/flat_part_charge_btree_index.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ydb/core/tablet_flat/flat_part_charge_btree_index.h b/ydb/core/tablet_flat/flat_part_charge_btree_index.h index 3796afdc0415..53fea5522abd 100644 --- a/ydb/core/tablet_flat/flat_part_charge_btree_index.h +++ b/ydb/core/tablet_flat/flat_part_charge_btree_index.h @@ -108,7 +108,7 @@ class TChargeBTreeIndex : public ICharge { }; const auto hasDataPage = [&](TNodeState& current, TRecIdx pos) -> bool { - return HasDataPage(current.GetShortChild(pos).PageId, { }); + return HasDataPage(current.GetShortChild(pos).PageId, { }); }; ready &= TryLoadRoot(meta, level); From 54ccfd8dad3ccfac2d8632924998cf855e5faae5 Mon Sep 17 00:00:00 2001 From: kungasc Date: Fri, 19 Jan 2024 12:05:00 +0000 Subject: [PATCH 16/20] fix tabs again --- ydb/core/tablet_flat/flat_part_charge_btree_index.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ydb/core/tablet_flat/flat_part_charge_btree_index.h b/ydb/core/tablet_flat/flat_part_charge_btree_index.h index 53fea5522abd..e3bf13247808 100644 --- a/ydb/core/tablet_flat/flat_part_charge_btree_index.h +++ b/ydb/core/tablet_flat/flat_part_charge_btree_index.h @@ -166,14 +166,14 @@ class TChargeBTreeIndex : public ICharge { key1PageId = level[i].GetShortChild(pos).PageId; // move row1 to the first key <= key1 row1 = Min(row1, level[i].GetShortChild(pos).RowCount - 1); - } + } if (level[i].PageId == key2PageId) { TRecIdx pos = level[i].SeekReverse(ESeek::Lower, key2, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); key2PageId = level[i].GetShortChild(pos).PageId; // move row2 to the first key > key2 if (pos) { row2 = Max(row2, level[i].GetShortChild(pos - 1).RowCount - 1); - // always charge row1, no matter what keys are + // always charge row1, no matter what keys are row2 = Min(row2, row1); } } From eac24ec834904909dd251787f2dc4a769119dea0 Mon Sep 17 00:00:00 2001 From: kungasc Date: Fri, 19 Jan 2024 15:54:00 +0000 Subject: [PATCH 17/20] fix charge tests --- ydb/core/tablet_flat/ut/ut_charge.cpp | 31 ++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/ydb/core/tablet_flat/ut/ut_charge.cpp b/ydb/core/tablet_flat/ut/ut_charge.cpp index ca0bf896af05..e5072d3a44b2 100644 --- a/ydb/core/tablet_flat/ut/ut_charge.cpp +++ b/ydb/core/tablet_flat/ut/ut_charge.cpp @@ -516,16 +516,18 @@ Y_UNIT_TEST_SUITE(Charge) { {TGroupId{2}, {}} }); + // key 0 transforms into row id 0 because it's before the slice first key 1 me.To(101).CheckByKeys(0, 9, 0, TMap{ {TGroupId{0}, {0, 1, 2, 3_I}}, - {TGroupId{1}, {0_g, 1, 2, 3_g}}, - {TGroupId{2}, {0_g, 1_g, 2_g, 3, 4, 5, 6_g}} + {TGroupId{1}, {0, 1, 2, 3_g}}, + {TGroupId{2}, {0, 1, 2, 3, 4, 5, 6_g}} }); + // key 1 also transforms into row id 0 because it's before the slice first key 1 me.To(102).CheckByKeys(1, 9, 0, TMap{ {TGroupId{0}, {0, 1, 2, 3_I}}, - {TGroupId{1}, {0_g, 1, 2, 3_g}}, - {TGroupId{2}, {0_g, 1_g, 2_g, 3, 4, 5, 6_g}} + {TGroupId{1}, {0, 1, 2, 3_g}}, + {TGroupId{2}, {0, 1, 2, 3, 4, 5, 6_g}} }); me.To(103).CheckByKeys(2, 9, 0, TMap{ @@ -688,10 +690,11 @@ Y_UNIT_TEST_SUITE(Charge) { {TGroupId{2}, {}} }); + // key 35 transforms into row id 26 because it's after the slice last key 35 me.To(301).CheckByKeys(27, 35, 0, TMap{ {TGroupId{0}, {6, 7, 8}}, - {TGroupId{1}, {10, 11, 12_g, 13_g}}, - {TGroupId{2}, {20_g, 21, 22, 23, 24_g, 25_g, 26_g}} + {TGroupId{1}, {10, 11, 12, 13}}, + {TGroupId{2}, {20_g, 21, 22, 23, 24, 25, 26}} }); me.To(302).CheckByKeys(27, 34, 0, TMap{ @@ -801,9 +804,11 @@ Y_UNIT_TEST_SUITE(Charge) { /*_ 1xx: custom spanned loads scenarios */ - me.To(101).CheckByKeys(0, 35, 8 /* rows */, { 0, 1, 2, 3_I }); - me.To(102).CheckByKeys(0, 35, 11 /* rows */, { 0, 1, 2, 3, 4_I }); - me.To(103).CheckByKeys(0, 35, 14 /* rows */, { 0, 1, 2, 3, 4, 5_I }); + // key 0 transforms into row id 0 because it's before the slice first key 1 + me.To(101).CheckByKeys(0, 35, 8 /* rows */, { 0, 1, 2 }); + me.To(102).CheckByKeys(0, 35, 11 /* rows */, { 0, 1, 2, 3 }); + me.To(103).CheckByKeys(0, 35, 14 /* rows */, { 0, 1, 2, 3, 4 }); + me.To(104).CheckByKeys(3, 35, 5 /* rows */, { 0, 1, 2 }); me.To(105).CheckByKeys(3, 35, 6 /* rows */, { 0, 1, 2, 3_I }); me.To(106).CheckByKeys(4, 35, 6 /* rows */, { 0, 1, 2, 3 }); @@ -815,7 +820,7 @@ Y_UNIT_TEST_SUITE(Charge) { /*_ 2xx: one row charge limit on two page */ - for (const ui16 page : xrange(4)) { + for (const ui16 page : xrange(1, 5)) { const TArr span1{ page, operator""_I(page + 1) }; const TArr span2{ page, page + 1 }; @@ -860,9 +865,10 @@ Y_UNIT_TEST_SUITE(Charge) { {TGroupId{2}, {11_g, 10_g, 9_g, 8, 7, 6, 5, 4, 3, 2_g}} }); + // key 1 transforms into row id 0 because it's before the slice first key 1 me.To(104).CheckByKeysReverse(15, 1, 0, TMap{ {TGroupId{0}, {3, 2, 1, 0}}, - {TGroupId{2}, {11_g, 10_g, 9_g, 8, 7, 6, 5, 4, 3, 2_g, 1_g, 0_g}} + {TGroupId{2}, {11_g, 10_g, 9_g, 8, 7, 6, 5, 4, 3, 2, 1, 0}} }); me.To(105).CheckByKeysReverse(15, 0, 0, TMap{ @@ -890,9 +896,10 @@ Y_UNIT_TEST_SUITE(Charge) { {TGroupId{2}, {26_g, 25_g, 24_g}} }); + // key 35 transforms into row id 26 because it's after the slice last key 35 me.To(110).CheckByKeysReverse(35, 32, 0, TMap{ {TGroupId{0}, {8, 7, 6_I}}, - {TGroupId{2}, {26_g, 25_g, 24_g}} + {TGroupId{2}, {26, 25, 24}} }); me.To(111).CheckByKeysReverse(4, 1, 0, TMap{ From b58553c25d69b127e7f9b8c219ab8930ca376248 Mon Sep 17 00:00:00 2001 From: kungasc Date: Fri, 19 Jan 2024 16:04:39 +0000 Subject: [PATCH 18/20] once again return last index key printing omg --- ydb/core/tablet_flat/flat_part_dump.cpp | 30 +++++++++++++++---------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/ydb/core/tablet_flat/flat_part_dump.cpp b/ydb/core/tablet_flat/flat_part_dump.cpp index d7a83f037305..f338d251f91e 100644 --- a/ydb/core/tablet_flat/flat_part_dump.cpp +++ b/ydb/core/tablet_flat/flat_part_dump.cpp @@ -122,7 +122,7 @@ namespace { Out << " + Index{" << (ui16)label.Type << " rev " << label.Format << ", " << label.Size << "b}" - << " " << index->Count << " rec" << Endl + << " " << index->Count + (index.GetLastKeyRecord() ? 1 : 0) << " rec" << Endl << " | Page Row Bytes ("; for (auto off : xrange(part.Scheme->Groups[0].KeyTypes.size())) { @@ -133,18 +133,8 @@ namespace { Out << ")" << Endl; - for (auto iter = index->Begin(); iter; iter++) { + auto printIndexKey = [&](const NPage::TIndex::TRecord* record) { key.clear(); - - if (depth < 2 && iter.Off() >= 10) { - Out - << " | -- skipped " << index->Count - iter.Off() - << " entries, depth level " << depth << Endl; - - break; - } - - auto record = iter.GetRecord(); for (const auto &info: part.Scheme->Groups[0].ColsKeyIdx) key.push_back(record->Cell(info)); @@ -161,6 +151,22 @@ namespace { Key(key, *part.Scheme); Out << Endl; + }; + + for (auto iter = index->Begin(); iter; iter++) { + if (depth < 2 && iter.Off() >= 10) { + Out + << " | -- skipped " << index->Count - iter.Off() + << " entries, depth level " << depth << Endl; + + break; + } + + printIndexKey(iter.GetRecord()); + } + + if (index.GetLastKeyRecord()) { + printIndexKey(index.GetLastKeyRecord()); } } From 56cf35824180b6fbf5ac6bde9780706f8de5ace2 Mon Sep 17 00:00:00 2001 From: kungasc Date: Fri, 19 Jan 2024 18:07:24 +0000 Subject: [PATCH 19/20] fix cut slices test --- ydb/core/tablet_flat/ut/ut_part.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/ydb/core/tablet_flat/ut/ut_part.cpp b/ydb/core/tablet_flat/ut/ut_part.cpp index 9d5c21c44758..51203cb2b0fc 100644 --- a/ydb/core/tablet_flat/ut/ut_part.cpp +++ b/ydb/core/tablet_flat/ut/ut_part.cpp @@ -952,7 +952,7 @@ Y_UNIT_TEST_SUITE(TPart) { return TSerializedCellVec(key); }; - auto slices = MakeIntrusive(); + TSlices slices; for (size_t rowId = 0; rowId < fullRows.size();) { TSlice slice; slice.FirstInclusive = true; @@ -963,16 +963,25 @@ Y_UNIT_TEST_SUITE(TPart) { slice.LastRowId = rowId + RandomNumber(2) + 1; if (slice.LastRowId < fullRows.size()) slice.LastKey = getKey(IndexTools::GetRecord(*cutPartTmp, slice.LastRowId)); - slices->push_back(slice); + slices.push_back(slice); rowId = slice.LastRowId; } Cerr << "======= SLICES =======" << Endl; - slices->Describe(Cerr); + slices.Describe(Cerr); Cerr << Endl; - TCheckIt cutWrap(cutCook.Finish(), { new TTouchEnv() }, slices), fullWrap(fullCook.Finish(), { new TTouchEnv() }); - TCheckReverseIt cutWrapR(cutCookR.Finish(), { new TTouchEnv() }, slices), fullWrapR(fullCookR.Finish(), { new TTouchEnv() }); + auto cutEggs = cutCook.Finish(), cutEggsR = cutCookR.Finish(); + for (auto& eggs : {cutEggs, cutEggsR}) { + auto partSlices = (TSlices*)eggs.Lone()->Slices.Get(); + partSlices->clear(); + for (auto s : slices) { + partSlices->push_back(s); + } + } + + TCheckIt cutWrap(cutEggs, { new TTouchEnv() }), fullWrap(fullCook.Finish(), { new TTouchEnv() }); + TCheckReverseIt cutWrapR(cutEggsR, { new TTouchEnv() }), fullWrapR(fullCookR.Finish(), { new TTouchEnv() }); auto cutPart = (*cutWrap).Eggs.Lone(); auto fullPart = (*fullWrap).Eggs.Lone(); @@ -984,6 +993,8 @@ Y_UNIT_TEST_SUITE(TPart) { Cerr << DumpPart(*fullPart, 2) << Endl; UNIT_ASSERT_GT(fullPart->IndexesRawSize, cutPart->IndexesRawSize); + UNIT_ASSERT_GT(cutPart->Slices->size(), 1); + UNIT_ASSERT_VALUES_EQUAL(fullPart->Slices->size(), 1); for (auto r : fullRows) { cutWrap.Has(*TSchemedCookRow(*lay).Col(r.first, r.second)); From 92564e8a673e9034bc46377f76b5deabc83b7b54 Mon Sep 17 00:00:00 2001 From: kungasc Date: Mon, 22 Jan 2024 17:00:44 +0000 Subject: [PATCH 20/20] cr: use const auto& --- ydb/core/tablet_flat/flat_part_charge_btree_index.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ydb/core/tablet_flat/flat_part_charge_btree_index.h b/ydb/core/tablet_flat/flat_part_charge_btree_index.h index e3bf13247808..9faff8d0872b 100644 --- a/ydb/core/tablet_flat/flat_part_charge_btree_index.h +++ b/ydb/core/tablet_flat/flat_part_charge_btree_index.h @@ -67,7 +67,7 @@ class TChargeBTreeIndex : public ICharge { TPageId key1PageId = key1 ? meta.PageId : Max(); TPageId key2PageId = key2 ? meta.PageId : Max(); - const auto iterateLevel = [&](std::function tryLoadNext) { + const auto iterateLevel = [&](const auto& tryLoadNext) { for (ui32 i : xrange(level.size())) { if (level[i].PageId == key1PageId) { TRecIdx pos = level[i].Seek(ESeek::Lower, key1, Scheme.Groups[0].ColsKeyIdx, &keyDefaults); @@ -159,7 +159,7 @@ class TChargeBTreeIndex : public ICharge { TPageId key1PageId = key1 ? meta.PageId : Max(); TPageId key2PageId = key2 ? meta.PageId : Max(); - const auto iterateLevel = [&](std::function tryLoadNext) { + const auto iterateLevel = [&](const auto& tryLoadNext) { for (ui32 i : xrange(level.size())) { if (level[i].PageId == key1PageId) { TRecIdx pos = level[i].SeekReverse(ESeek::Lower, key1, Scheme.Groups[0].ColsKeyIdx, &keyDefaults);