summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorijon <[email protected]>2026-07-20 13:53:08 +0300
committerGitHub <[email protected]>2026-07-20 13:53:08 +0300
commit4a157b7fbc3959d801cdb6fe9ca2ceb99becd9fe (patch)
tree6873db049a067d0f6153112eafa8f0fb1c909c57
parentd68f70459cac3cb95da03d0ab79701d59da527f6 (diff)
schemeshard: extend move-with-replace test with src-dst combinations (#46838)
-rw-r--r--ydb/core/tx/schemeshard/ut_move/ut_move.cpp322
1 files changed, 207 insertions, 115 deletions
diff --git a/ydb/core/tx/schemeshard/ut_move/ut_move.cpp b/ydb/core/tx/schemeshard/ut_move/ut_move.cpp
index 4a6316f3bc7..c138eab5556 100644
--- a/ydb/core/tx/schemeshard/ut_move/ut_move.cpp
+++ b/ydb/core/tx/schemeshard/ut_move/ut_move.cpp
@@ -365,147 +365,239 @@ Y_UNIT_TEST_SUITE(TSchemeShardMoveTest) {
NLs::ShardsInsideDomain(2)});
}
- Y_UNIT_TEST(Replace) {
- TTestBasicRuntime runtime;
- TTestEnv env(runtime);
- ui64 txId = 100;
+ // Move-with-replace test. Machinery for parametrized test.
- auto initialDomainDesc = DescribePath(runtime, "/MyRoot");
- ui64 expectedDomainPaths = initialDomainDesc.GetPathDescription().GetDomainDescription().GetPathsInside();
+ // Objects creation and verification
- TestCreateIndexedTable(runtime, ++txId, "/MyRoot", R"(
- TableDescription {
- Name: "Src"
- Columns { Name: "key" Type: "Uint64" }
- Columns { Name: "value0" Type: "Utf8" }
- Columns { Name: "value1" Type: "Utf8" }
- KeyColumnNames: ["key"]
- }
- IndexDescription {
- Name: "Sync"
- KeyColumnNames: ["value0"]
- }
- IndexDescription {
- Name: "Async"
- KeyColumnNames: ["value1"]
- Type: EIndexTypeGlobalAsync
- }
- )");
- env.TestWaitNotification(runtime, txId);
+ using CreateRequestMethod = TEvSchemeShard::TEvModifySchemeTransaction* (*)(const TString& workingDir, const TString& name);
+ using IdentityChecksMethod = TVector<NLs::TCheckFunc> (*)(const TString& name);
- expectedDomainPaths += 5;
+ struct TCreatePathOp {
+ // Scheme object create request generator
+ CreateRequestMethod CreateRequest;
+ // Path and shard count that will be created by creating the object
+ ui32 PathCount = 1;
+ ui32 ShardCount = 1;
+ // Some identity check for the created object (to verify result path after replacement)
+ IdentityChecksMethod CreateIdentityChecks;
+ };
- TestCreateIndexedTable(runtime, ++txId, "/MyRoot", R"(
- TableDescription {
- Name: "Dst"
- Columns { Name: "key" Type: "Uint64" }
- Columns { Name: "value0" Type: "Utf8" }
- Columns { Name: "value1" Type: "Utf8" }
- KeyColumnNames: ["key"]
+ TCreatePathOp CreateOpRowTableWithIndexes() {
+ return {
+ .CreateRequest = [](const TString& workingDir, const TString& name) {
+ const TString modifyScheme = Sprintf(
+ R"(
+ TableDescription {
+ Name: "%s"
+ Columns { Name: "key" Type: "Uint64" }
+ Columns { Name: "value0" Type: "Utf8" }
+ Columns { Name: "value1" Type: "Utf8" }
+ KeyColumnNames: ["key"]
+ }
+ IndexDescription {
+ Name: "Sync"
+ KeyColumnNames: ["value0"]
+ }
+ IndexDescription {
+ Name: "Async"
+ KeyColumnNames: ["value1"]
+ Type: EIndexTypeGlobalAsync
+ }
+ )",
+ name.c_str()
+ );
+ return CreateIndexedTableRequest(0 /* txId */, workingDir, modifyScheme);
+ },
+ .PathCount = 5,
+ .ShardCount = 3,
+ .CreateIdentityChecks = [](const TString& name) -> TVector<NLs::TCheckFunc> {
+ return {
+ NLs::IsTable,
+ NLs::CheckColumns(name, {"key", "value0", "value1"}, {}, {"key"}, /*strictCount*/ true),
+ NLs::IndexesCount(2),
+ // NLs::ChildrenCount(2),
+ };
}
- IndexDescription {
- Name: "Sync"
- KeyColumnNames: ["value0"]
+ };
+ }
+
+ TCreatePathOp CreateOpColumnTable() {
+ return {
+ .CreateRequest = [](const TString& workingDir, const TString& name) {
+ const TString modifyScheme = Sprintf(
+ R"(
+ Name: "%s"
+ ColumnShardCount: 1
+ Schema {
+ Columns { Name: "timestamp" Type: "Timestamp" NotNull: true }
+ Columns { Name: "resource_id" Type: "Utf8" }
+ Columns { Name: "uid" Type: "Utf8" NotNull: true }
+ KeyColumnNames: "timestamp"
+ KeyColumnNames: "uid"
+ }
+ )",
+ name.c_str()
+ );
+ return CreateColumnTableRequest(0 /* txId */, workingDir, modifyScheme);
+ },
+ .PathCount = 1,
+ .ShardCount = 1,
+ .CreateIdentityChecks = [](const TString& /*name*/) -> TVector<NLs::TCheckFunc> {
+ return {
+ NLs::IsColumnTable,
+ NLs::ColumnTableIndexesCount(0),
+ NLs::ChildrenCount(0),
+ };
}
- IndexDescription {
- Name: "Async"
- KeyColumnNames: ["value1"]
- Type: EIndexTypeGlobalAsync
+ };
+ }
+
+ TCreatePathOp CreateOpColumnTableWithIndexes() {
+ // Advanced case from the start: table with (local) indexes
+ return {
+ .CreateRequest = [](const TString& workingDir, const TString& name) {
+ const TString modifyScheme = NLocalIndexes::OlapTableWithBloomAndNgramIndexes(name);
+ return CreateColumnTableRequest(0 /* txId */, workingDir, modifyScheme);
+ },
+ .PathCount = 3,
+ .ShardCount = 1,
+ .CreateIdentityChecks = [](const TString& /*name*/) -> TVector<NLs::TCheckFunc> {
+ return {
+ NLs::IsColumnTable,
+ NLs::ColumnTableIndexesCount(2),
+ NLs::ChildrenCount(2),
+ };
}
- )");
- env.TestWaitNotification(runtime, txId);
+ };
+ }
- expectedDomainPaths += 5;
+ enum EMoveReplaceTestPathType {
+ RowTable,
+ ColumnTable,
+ ColumnTableWithIndexes,
+ //TODO: extend to IndexImplTable and non-tables like Directory and Topic
+ };
- TestDescribeResult(DescribePath(runtime, "/MyRoot"),
- {NLs::ChildrenCount(3),
- NLs::PathsInsideDomain(expectedDomainPaths),
- NLs::ShardsInsideDomain(6)});
+ TCreatePathOp PathCreateOp(EMoveReplaceTestPathType pathType) {
+ switch (pathType) {
+ case EMoveReplaceTestPathType::RowTable: return CreateOpRowTableWithIndexes();
+ case EMoveReplaceTestPathType::ColumnTable: return CreateOpColumnTable();
+ case EMoveReplaceTestPathType::ColumnTableWithIndexes: return CreateOpColumnTableWithIndexes();
+ }
+ }
- TLocalPathId movedTablePathId = GetNextLocalPathId(runtime, txId);
- {
- ++txId;
- auto first = DropTableRequest(txId, "/MyRoot", "Dst");
- auto second = MoveTableRequest(txId, "/MyRoot/Src", "/MyRoot/Dst");
- auto combination = CombineSchemeTransactions({first, second});
- AsyncSend(runtime, TTestTxConfig::SchemeShard, combination);
- TestModificationResult(runtime, txId);
- env.TestWaitNotification(runtime, txId);
+ void TestCreateObject(const TCreatePathOp& op, TTestActorRuntime& runtime, ui64 txId, const TString& workingDir, const TString& name) {
+ auto* ev = op.CreateRequest(workingDir, name);
- expectedDomainPaths -= 5;
- }
+ NKikimrScheme::TEvModifySchemeTransaction& t = ev->Record;
+ t.SetTxId(txId);
+ t.SetTabletId(TTestTxConfig::SchemeShard);
- env.TestWaitTabletDeletion(runtime, xrange(TTestTxConfig::FakeHiveTablets+3, TTestTxConfig::FakeHiveTablets+6));
+ AsyncSend(runtime, TTestTxConfig::SchemeShard, ev);
+ TestModificationResults(runtime, txId, {{NKikimrScheme::StatusAccepted}});
+ }
+ void TestMoveReplace(TTestActorRuntime& runtime, ui64 txId, const TString& srcPath, const TString& dstPath) {
+ //NOTE: DropTableRequest generates ESchemeOpDropTable operation, which is row table specific
+ // but will work here in general way for any table type due to hackish/magical way
+ // drop-table is implemented (see CreateDropIndexedTable for details)
+ auto* dstDrop = DropTableRequest(txId, TString(ExtractParent(dstPath)), TString(ExtractBase(dstPath)));
+ auto* srcMove = MoveTableRequest(txId, srcPath, dstPath);
+ AsyncSend(runtime, TTestTxConfig::SchemeShard, CombineSchemeTransactions({dstDrop, srcMove}));
+ TestModificationResults(runtime, txId, {{NKikimrScheme::StatusAccepted}});
+ }
- TestDescribeResult(DescribePath(runtime, "/MyRoot/Src"),
- {NLs::PathNotExist});
+ // Replace test. Parametrized test body
- TestDescribeResult(DescribePath(runtime, "/MyRoot/Dst"),
- {NLs::IsTable,
- NLs::PathIdEqual(movedTablePathId),
- NLs::PathVersionEqual(5),
- NLs::CheckColumns("Dst", {"key", "value0", "value1"}, {}, {"key"}),
- NLs::IndexesCount(2)});
+ struct TMoveReplaceTestCase {
+ TString Tag;
+ EMoveReplaceTestPathType SrcType;
+ EMoveReplaceTestPathType DstType;
+ };
- TestDescribeResult(DescribePath(runtime, "/MyRoot"),
- {NLs::ChildrenCount(2),
- NLs::PathsInsideDomain(expectedDomainPaths),
- NLs::ShardsInsideDomain(3)});
+ void MoveReplaceTest(const TMoveReplaceTestCase& params) {
+ TTestBasicRuntime runtime;
+ TTestEnv env(runtime);
+ runtime.GetAppData().FeatureFlags.SetEnableMoveColumnTable(true);
+ runtime.GetAppData().FeatureFlags.SetEnableLocalIndexAsSchemeObject(true);
+ ui64 txId = 100;
- TestCreateIndexedTable(runtime, ++txId, "/MyRoot", R"(
- TableDescription {
- Name: "Src"
- Columns { Name: "key" Type: "Uint64" }
- Columns { Name: "value0" Type: "Utf8" }
- Columns { Name: "value1" Type: "Utf8" }
- KeyColumnNames: ["key"]
- }
- IndexDescription {
- Name: "Sync"
- KeyColumnNames: ["value0"]
- }
- IndexDescription {
- Name: "Async"
- KeyColumnNames: ["value1"]
- Type: EIndexTypeGlobalAsync
- }
- )");
- env.TestWaitNotification(runtime, txId);
+ // to accommodate automatic sysviews creation
+ ui64 initialPathCount = DescribePath(runtime, "/MyRoot").GetPathDescription().GetDomainDescription().GetPathsInside();
- expectedDomainPaths += 5;
+ // Create Src and Dst objects, move Src over Dst, check the result.
+ // Prepare phase.
+ const auto& dstCreateOp = PathCreateOp(params.DstType);
+ const auto& srcCreateOp = PathCreateOp(params.SrcType);
- movedTablePathId = GetNextLocalPathId(runtime, txId);
- {
- ++txId;
- auto first = DropTableRequest(txId, "/MyRoot", "Dst");
- auto second = MoveTableRequest(txId, "/MyRoot/Src", "/MyRoot/Dst");
- auto combination = CombineSchemeTransactions({first, second});
- AsyncSend(runtime, TTestTxConfig::SchemeShard, combination);
- TestModificationResult(runtime, txId);
- env.TestWaitNotification(runtime, txId);
+ TestCreateObject(dstCreateOp, runtime, ++txId, "/MyRoot", "Dst");
+ TestCreateObject(srcCreateOp, runtime, ++txId, "/MyRoot", "Src");
+ env.TestWaitNotification(runtime, {txId - 1, txId});
- expectedDomainPaths -= 5;
- }
+ // database contains both src and dst paths and shards
+ TestDescribeResult(DescribePath(runtime, "/MyRoot"), {
+ NLs::ChildrenCount(3),
+ NLs::PathsInsideDomain(initialPathCount + dstCreateOp.PathCount + srcCreateOp.PathCount),
+ NLs::ShardsInsideDomain(dstCreateOp.ShardCount + srcCreateOp.ShardCount),
+ });
- env.TestWaitTabletDeletion(runtime, xrange(TTestTxConfig::FakeHiveTablets, TTestTxConfig::FakeHiveTablets+3));
+ // Test body.
+ TLocalPathId movedTablePathId = GetNextLocalPathId(runtime, txId);
+ TestMoveReplace(runtime, ++txId, "/MyRoot/Src", "/MyRoot/Dst");
+ env.TestWaitNotification(runtime, txId);
- TestDescribeResult(DescribePath(runtime, "/MyRoot/Src"),
- {NLs::PathNotExist});
+ // Result check phase.
+ // dst shards should be removed
+ env.TestWaitTabletDeletion(runtime, xrange(TTestTxConfig::FakeHiveTablets, TTestTxConfig::FakeHiveTablets + dstCreateOp.ShardCount));
- TestDescribeResult(DescribePath(runtime, "/MyRoot/Dst"),
- {NLs::IsTable,
- NLs::PathIdEqual(movedTablePathId),
- NLs::PathVersionEqual(5),
- NLs::CheckColumns("Dst", {"key", "value0", "value1"}, {}, {"key"}),
- NLs::IndexesCount(2)});
+ // src path should be removed
+ TestDescribeResult(DescribePath(runtime, "/MyRoot/Src"), {NLs::PathNotExist});
- TestDescribeResult(DescribePath(runtime, "/MyRoot"),
- {NLs::ChildrenCount(2),
- NLs::PathsInsideDomain(expectedDomainPaths),
- NLs::ShardsInsideDomain(3)});
+ // dst should exist and be the Src
+ {
+ const auto& dst = DescribePath(runtime, "/MyRoot/Dst");
+ TestDescribeResult(dst, {
+ NLs::PathExist,
+ NLs::PathIdEqual(movedTablePathId),
+ //FIXME: NLs::PathVersionEqual(5),
+ });
+ TestDescribeResult(dst, srcCreateOp.CreateIdentityChecks("Dst"));
+ }
+
+ // database should contain only new dst, no traces of src
+ TestDescribeResult(DescribePath(runtime, "/MyRoot"), {
+ NLs::ChildrenCount(2), // .sys + Dst
+ NLs::PathsInsideDomain(initialPathCount + srcCreateOp.PathCount),
+ NLs::ShardsInsideDomain(srcCreateOp.ShardCount)
+ });
}
+ // MoveReplace test. Parametrized test
+
+ //TODO: switch to iteration through all possible pairs when all variants will work
+ static const std::vector<TMoveReplaceTestCase> MoveReplaceTests = {
+ { .Tag = "RowTable-over-RowTable", .SrcType = RowTable, .DstType = RowTable },
+ // { .Tag = "ColumnTable-over-ColumnTable", .SrcType = ColumnTable, .DstType = ColumnTable },
+ // { .Tag = "ColumnTableWithIndexes-over-ColumnTableWithIndexes", .SrcType = ColumnTableWithIndexes, .DstType = ColumnTableWithIndexes },
+ // { .Tag = "RowTable-over-ColumnTable", .SrcType = RowTable, .DstType = ColumnTable },
+ { .Tag = "ColumnTable-over-RowTable", .SrcType = ColumnTable, .DstType = RowTable },
+ };
+ struct TTestRegistration_MoveReplace {
+ TTestRegistration_MoveReplace() {
+ static std::vector<TString> TestNames;
+ TestNames.reserve(MoveReplaceTests.size());
+ for (const auto& param : MoveReplaceTests) {
+ TestNames.emplace_back(TStringBuilder() << "Move-" << param.Tag);
+ TCurrentTest::AddTest(
+ TestNames.back().c_str(),
+ std::bind(std::bind(MoveReplaceTest, param), std::placeholders::_1),
+ /*forceFork*/ false
+ );
+ }
+ }
+ };
+ static TTestRegistration_MoveReplace testRegistration_MoveReplace;
+
Y_UNIT_TEST(Replace2) {
TTestBasicRuntime runtime;
TTestEnv env(runtime);