diff options
| author | ijon <[email protected]> | 2026-07-20 13:53:08 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-07-20 13:53:08 +0300 |
| commit | 4a157b7fbc3959d801cdb6fe9ca2ceb99becd9fe (patch) | |
| tree | 6873db049a067d0f6153112eafa8f0fb1c909c57 | |
| parent | d68f70459cac3cb95da03d0ab79701d59da527f6 (diff) | |
schemeshard: extend move-with-replace test with src-dst combinations (#46838)
| -rw-r--r-- | ydb/core/tx/schemeshard/ut_move/ut_move.cpp | 322 |
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); |
