|
@@ -2,6 +2,7 @@
|
|
#include "flat_part_btree_index_iter.h"
|
|
#include "flat_part_btree_index_iter.h"
|
|
#include "flat_part_charge.h"
|
|
#include "flat_part_charge.h"
|
|
#include "flat_part_charge_btree_index.h"
|
|
#include "flat_part_charge_btree_index.h"
|
|
|
|
+#include "flat_part_charge_range.h"
|
|
#include "flat_part_iter_multi.h"
|
|
#include "flat_part_iter_multi.h"
|
|
#include "test/libs/table/test_writer.h"
|
|
#include "test/libs/table/test_writer.h"
|
|
#include <ydb/core/tablet_flat/test/libs/rows/layout.h>
|
|
#include <ydb/core/tablet_flat/test/libs/rows/layout.h>
|
|
@@ -38,7 +39,7 @@ namespace {
|
|
TMap<TGroupId, TSet<TPageId>> Touched;
|
|
TMap<TGroupId, TSet<TPageId>> 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, bool allowFirstLastPageDifference = false) {
|
|
TSet<TGroupId> groupIds;
|
|
TSet<TGroupId> groupIds;
|
|
for (const auto &c : {bTree.Loaded, flat.Loaded}) {
|
|
for (const auto &c : {bTree.Loaded, flat.Loaded}) {
|
|
for (const auto &g : c) {
|
|
for (const auto &g : c) {
|
|
@@ -59,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);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -111,7 +122,7 @@ namespace {
|
|
}
|
|
}
|
|
return TSerializedCellVec(key);
|
|
return TSerializedCellVec(key);
|
|
};
|
|
};
|
|
- auto add = [&](TRowId pageIndex1 /*inclusive*/, TRowId pageIndex2 /*exclusive*/) {
|
|
|
|
|
|
+ auto add = [&](ui32 pageIndex1 /*inclusive*/, ui32 pageIndex2 /*exclusive*/) {
|
|
TSlice slice;
|
|
TSlice slice;
|
|
slice.FirstInclusive = true;
|
|
slice.FirstInclusive = true;
|
|
slice.FirstRowId = pageIndex1 * 2;
|
|
slice.FirstRowId = pageIndex1 * 2;
|
|
@@ -366,12 +377,12 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) {
|
|
Y_UNREACHABLE();
|
|
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) {
|
|
bool reverse, const TKeyCellDefaults &keyDefaults, const TString& message, ui32 failsAllowed = 10) {
|
|
while (true) {
|
|
while (true) {
|
|
auto result = reverse
|
|
auto result = reverse
|
|
- ? charge.DoReverse(key1, key2, 0, Max<TRowId>(), keyDefaults, itemsLimit, bytesLimit)
|
|
|
|
- : charge.Do(key1, key2, 0, Max<TRowId>(), 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) {
|
|
if (result.Ready) {
|
|
return result.Overshot;
|
|
return result.Overshot;
|
|
}
|
|
}
|
|
@@ -382,8 +393,8 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) {
|
|
}
|
|
}
|
|
|
|
|
|
void CheckChargeRowId(const TPartStore& part, TTagsRef tags, const TKeyCellDefaults *keyDefaults, bool reverse) {
|
|
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;
|
|
TTouchEnv bTreeEnv, flatEnv;
|
|
TChargeBTreeIndex bTree(&bTreeEnv, part, tags, true);
|
|
TChargeBTreeIndex bTree(&bTreeEnv, part, tags, true);
|
|
TCharge flat(&flatEnv, part, tags, true);
|
|
TCharge flat(&flatEnv, part, tags, true);
|
|
@@ -391,7 +402,7 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) {
|
|
TString message = TStringBuilder() << (reverse ? "ChargeRowIdReverse " : "ChargeRowId ") << rowId1 << " " << rowId2;
|
|
TString message = TStringBuilder() << (reverse ? "ChargeRowIdReverse " : "ChargeRowId ") << rowId1 << " " << rowId2;
|
|
DoChargeRowId(bTree, bTreeEnv, rowId1, rowId2, 0, 0, reverse, *keyDefaults, message);
|
|
DoChargeRowId(bTree, bTreeEnv, rowId1, rowId2, 0, 0, reverse, *keyDefaults, message);
|
|
DoChargeRowId(flat, flatEnv, 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -418,15 +429,11 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) {
|
|
}
|
|
}
|
|
message << ")";
|
|
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);
|
|
|
|
-
|
|
|
|
- AssertLoadTheSame(part, bTreeEnv, flatEnv, message);
|
|
|
|
|
|
+ UNIT_ASSERT_VALUES_EQUAL_C(bTreeOvershot, flatOvershot, message);
|
|
|
|
+ AssertLoadedTheSame(part, bTreeEnv, flatEnv, message, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -444,10 +451,8 @@ Y_UNIT_TEST_SUITE(TChargeBTreeIndex) {
|
|
|
|
|
|
CheckChargeRowId(part, tags, eggs.Scheme->Keys.Get(), false);
|
|
CheckChargeRowId(part, tags, eggs.Scheme->Keys.Get(), false);
|
|
CheckChargeRowId(part, tags, eggs.Scheme->Keys.Get(), true);
|
|
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
|
|
|
|
|
|
+ CheckChargeKeys(part, tags, eggs.Scheme->Keys.Get(), false);
|
|
|
|
+ CheckChargeKeys(part, tags, eggs.Scheme->Keys.Get(), true);
|
|
}
|
|
}
|
|
|
|
|
|
Y_UNIT_TEST(NoNodes) {
|
|
Y_UNIT_TEST(NoNodes) {
|
|
@@ -470,7 +475,7 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) {
|
|
UNIT_ASSERT_VALUES_EQUAL_C(bTree.GetRowId(), flat.GetRowId(), 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) {
|
|
|
|
|
|
+ EReady Seek(TRunIt& iter, TTouchEnv& env, ESeek seek, bool reverse, TCells key, const TString& message, ui32 failsAllowed = 10) {
|
|
return Retry([&]() {
|
|
return Retry([&]() {
|
|
return reverse ? iter.SeekReverse(key, seek) : iter.Seek(key, seek);
|
|
return reverse ? iter.SeekReverse(key, seek) : iter.Seek(key, seek);
|
|
}, env, message, failsAllowed);
|
|
}, env, message, failsAllowed);
|
|
@@ -482,7 +487,22 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) {
|
|
}, env, message, failsAllowed);
|
|
}, env, message, failsAllowed);
|
|
}
|
|
}
|
|
|
|
|
|
- void CheckIterateKey(const TPartEggs& eggs) {
|
|
|
|
|
|
+ void Charge(const TRun &run, const TVector<TTag> 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();
|
|
const auto part = *eggs.Lone();
|
|
|
|
|
|
TRun btreeRun(*eggs.Scheme->Keys), flatRun(*eggs.Scheme->Keys);
|
|
TRun btreeRun(*eggs.Scheme->Keys), flatRun(*eggs.Scheme->Keys);
|
|
@@ -512,18 +532,18 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) {
|
|
TRunIt bTree(btreeRun, tags, eggs.Scheme->Keys, &bTreeEnv);
|
|
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) {
|
|
for (auto c : key) {
|
|
message << c.AsValue<ui32>() << " ";
|
|
message << c.AsValue<ui32>() << " ";
|
|
}
|
|
}
|
|
- 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);
|
|
AssertEqual(bTree, bTreeReady, flat, flatReady, message);
|
|
- AssertLoadTheSame(part, bTreeEnv, flatEnv, message);
|
|
|
|
|
|
+ AssertLoadedTheSame(part, bTreeEnv, flatEnv, message);
|
|
}
|
|
}
|
|
|
|
|
|
for (ui32 steps = 1; steps <= 10; steps++) {
|
|
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) {
|
|
for (auto c : key) {
|
|
message << c.AsValue<ui32>() << " ";
|
|
message << c.AsValue<ui32>() << " ";
|
|
}
|
|
}
|
|
@@ -531,7 +551,58 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) {
|
|
EReady bTreeReady = Next(bTree, bTreeEnv, reverse, message);
|
|
EReady bTreeReady = Next(bTree, bTreeEnv, reverse, message);
|
|
EReady flatReady = Next(flat, flatEnv, reverse, message);
|
|
EReady flatReady = Next(flat, flatEnv, reverse, message);
|
|
AssertEqual(bTree, bTreeReady, flat, flatReady, message);
|
|
AssertEqual(bTree, bTreeReady, flat, flatReady, message);
|
|
- AssertLoadTheSame(part, bTreeEnv, flatEnv, message);
|
|
|
|
|
|
+ AssertLoadedTheSame(part, bTreeEnv, flatEnv, message);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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<TBtreeIndexMeta>*)&flatPart->IndexPages.BTreeGroups;
|
|
|
|
+ pages->clear();
|
|
|
|
+ pages = (TVector<TBtreeIndexMeta>*)&flatPart->IndexPages.BTreeHistoric;
|
|
|
|
+ pages->clear();
|
|
|
|
+ flatRun.Insert(flatPart, slice);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ auto tags = TVector<TTag>();
|
|
|
|
+ for (auto c : eggs.Scheme->Cols) {
|
|
|
|
+ tags.push_back(c.Tag);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (bool reverse : {false, true}) {
|
|
|
|
+ for (ui32 firstCellKey1 : xrange<ui32>(0, part.Stat.Rows / 7 + 1)) {
|
|
|
|
+ for (ui32 secondCellKey1 : xrange<ui32>(0, 14)) {
|
|
|
|
+ for (ui32 firstCellKey2 : xrange<ui32>(0, part.Stat.Rows / 7 + 1)) {
|
|
|
|
+ for (ui32 secondCellKey2 : xrange<ui32>(0, 14)) {
|
|
|
|
+ TVector<TCell> key1 = MakeKey(firstCellKey1, secondCellKey1);
|
|
|
|
+ TVector<TCell> key2 = MakeKey(firstCellKey2, secondCellKey2);
|
|
|
|
+
|
|
|
|
+ TTouchEnv bTreeEnv, flatEnv;
|
|
|
|
+
|
|
|
|
+ TStringBuilder message = TStringBuilder() << (reverse ? "ChargeReverse " : "Charge ") << "(";
|
|
|
|
+ for (auto c : key1) {
|
|
|
|
+ message << c.AsValue<ui32>() << " ";
|
|
|
|
+ }
|
|
|
|
+ message << ") (";
|
|
|
|
+ for (auto c : key2) {
|
|
|
|
+ message << c.AsValue<ui32>() << " ";
|
|
|
|
+ }
|
|
|
|
+ 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);
|
|
|
|
+
|
|
|
|
+ AssertLoadedTheSame(part, bTreeEnv, flatEnv, message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -543,7 +614,8 @@ Y_UNIT_TEST_SUITE(TPartBtreeIndexIteration) {
|
|
TPartEggs eggs = MakePart(slices, levels);
|
|
TPartEggs eggs = MakePart(slices, levels);
|
|
const auto part = *eggs.Lone();
|
|
const auto part = *eggs.Lone();
|
|
|
|
|
|
- CheckIterateKey(eggs);
|
|
|
|
|
|
+ CheckIterate(eggs);
|
|
|
|
+ CheckCharge(eggs);
|
|
}
|
|
}
|
|
|
|
|
|
Y_UNIT_TEST(NoNodes_SingleSlice) {
|
|
Y_UNIT_TEST(NoNodes_SingleSlice) {
|