Skip to content

Commit 93f64f6

Browse files
authored
24-3: Index build: do not lose the requested partitioning settings of an indexImplTable in case of SchemeShard reboots (#10579) (#10634)
1 parent 64c159d commit 93f64f6

File tree

12 files changed

+242
-17
lines changed

12 files changed

+242
-17
lines changed

ydb/core/tx/schemeshard/schemeshard_build_index.cpp

+13-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ void TSchemeShard::Handle(TEvPrivate::TEvIndexBuildingMakeABill::TPtr& ev, const
3434

3535
void TSchemeShard::PersistCreateBuildIndex(NIceDb::TNiceDb& db, const TIndexBuildInfo::TPtr info) {
3636
Y_ABORT_UNLESS(info->BuildKind != TIndexBuildInfo::EBuildKind::BuildKindUnspecified);
37-
db.Table<Schema::IndexBuild>().Key(info->Id).Update(
37+
auto persistedBuildIndex = db.Table<Schema::IndexBuild>().Key(info->Id);
38+
persistedBuildIndex.Update(
3839
NIceDb::TUpdate<Schema::IndexBuild::Uid>(info->Uid),
3940
NIceDb::TUpdate<Schema::IndexBuild::DomainOwnerId>(info->DomainPathId.OwnerId),
4041
NIceDb::TUpdate<Schema::IndexBuild::DomainLocalId>(info->DomainPathId.LocalPathId),
@@ -48,6 +49,17 @@ void TSchemeShard::PersistCreateBuildIndex(NIceDb::TNiceDb& db, const TIndexBuil
4849
NIceDb::TUpdate<Schema::IndexBuild::MaxRetries>(info->Limits.MaxRetries),
4950
NIceDb::TUpdate<Schema::IndexBuild::BuildKind>(ui32(info->BuildKind))
5051
);
52+
// Persist details of the index build operation: ImplTableDescription.
53+
// We have chosen TIndexCreationConfig's string representation as the serialization format.
54+
{
55+
NKikimrSchemeOp::TIndexCreationConfig serializableRepresentation;
56+
57+
*serializableRepresentation.MutableIndexImplTableDescription() = info->ImplTableDescription;
58+
59+
persistedBuildIndex.Update(
60+
NIceDb::TUpdate<Schema::IndexBuild::CreationConfig>(serializableRepresentation.SerializeAsString())
61+
);
62+
}
5163

5264
ui32 columnNo = 0;
5365
for (ui32 i = 0; i < info->IndexColumns.size(); ++i, ++columnNo) {

ydb/core/tx/schemeshard/schemeshard_info_types.h

+8
Original file line numberDiff line numberDiff line change
@@ -3092,6 +3092,14 @@ struct TIndexBuildInfo: public TSimpleRefCount<TIndexBuildInfo> {
30923092
indexInfo->IndexName = row.template GetValue<Schema::IndexBuild::IndexName>();
30933093
indexInfo->IndexType = row.template GetValue<Schema::IndexBuild::IndexType>();
30943094

3095+
// Restore the operation details: ImplTableDescription.
3096+
if (row.template HaveValue<Schema::IndexBuild::CreationConfig>()) {
3097+
NKikimrSchemeOp::TIndexCreationConfig creationConfig;
3098+
Y_ABORT_UNLESS(creationConfig.ParseFromString(row.template GetValue<Schema::IndexBuild::CreationConfig>()));
3099+
3100+
indexInfo->ImplTableDescription = std::move(*creationConfig.MutableIndexImplTableDescription());
3101+
}
3102+
30953103
indexInfo->State = TIndexBuildInfo::EState(
30963104
row.template GetValue<Schema::IndexBuild::State>());
30973105
indexInfo->Issue =

ydb/core/tx/schemeshard/schemeshard_schema.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,9 @@ struct Schema : NIceDb::Schema {
13181318
struct AlterMainTableTxStatus : Column<32, NScheme::NTypeIds::Uint32> { using Type = NKikimrScheme::EStatus; };
13191319
struct AlterMainTableTxDone : Column<33, NScheme::NTypeIds::Bool> {};
13201320

1321+
// Serialized as string NKikimrSchemeOp::TIndexCreationConfig protobuf.
1322+
struct CreationConfig : Column<34, NScheme::NTypeIds::String> { using Type = TString; };
1323+
13211324
using TKey = TableKey<Id>;
13221325
using TColumns = TableColumns<
13231326
Id,
@@ -1352,7 +1355,8 @@ struct Schema : NIceDb::Schema {
13521355
BuildKind,
13531356
AlterMainTableTxId,
13541357
AlterMainTableTxStatus,
1355-
AlterMainTableTxDone
1358+
AlterMainTableTxDone,
1359+
CreationConfig
13561360
>;
13571361
};
13581362

ydb/core/tx/schemeshard/ut_helpers/ls_checks.cpp

+53-1
Original file line numberDiff line numberDiff line change
@@ -1252,11 +1252,63 @@ TCheckFunc PartitionKeys(TVector<TString> lastShardKeys) {
12521252
const auto& pathDescr = record.GetPathDescription();
12531253
UNIT_ASSERT_VALUES_EQUAL(lastShardKeys.size(), pathDescr.TablePartitionsSize());
12541254
for (size_t i = 0; i < lastShardKeys.size(); ++i) {
1255-
UNIT_ASSERT_STRING_CONTAINS(pathDescr.GetTablePartitions(i).GetEndOfRangeKeyPrefix(), lastShardKeys[i]);
1255+
const auto& partition = pathDescr.GetTablePartitions(i);
1256+
UNIT_ASSERT_STRING_CONTAINS_C(
1257+
partition.GetEndOfRangeKeyPrefix(), lastShardKeys[i],
1258+
"partition index: " << i << '\n'
1259+
<< "actual key prefix: " << partition.GetEndOfRangeKeyPrefix().Quote() << '\n'
1260+
<< "expected key prefix: " << lastShardKeys[i].Quote() << '\n'
1261+
);
12561262
}
12571263
};
12581264
}
12591265

1266+
namespace {
1267+
1268+
// Serializes / deserializes a value of type T to a cell vector string representation.
1269+
template <typename T>
1270+
struct TSplitBoundarySerializer {
1271+
static TString Serialize(T splitBoundary) {
1272+
const auto cell = TCell::Make(splitBoundary);
1273+
TSerializedCellVec cellVec(TArrayRef<const TCell>(&cell, 1));
1274+
return cellVec.ReleaseBuffer();
1275+
}
1276+
1277+
static TVector<T> Deserialize(const TString& serializedCells) {
1278+
TSerializedCellVec cells(serializedCells);
1279+
TVector<T> values;
1280+
for (const auto& cell : cells.GetCells()) {
1281+
if (cell.IsNull()) {
1282+
// the last cell
1283+
break;
1284+
}
1285+
values.emplace_back(cell.AsValue<T>());
1286+
}
1287+
return values;
1288+
}
1289+
};
1290+
1291+
}
1292+
1293+
template <typename T>
1294+
TCheckFunc SplitBoundaries(TVector<T>&& expectedBoundaries) {
1295+
return [expectedBoundaries = std::move(expectedBoundaries)] (const NKikimrScheme::TEvDescribeSchemeResult& record) {
1296+
const auto& pathDescr = record.GetPathDescription();
1297+
UNIT_ASSERT_VALUES_EQUAL(pathDescr.TablePartitionsSize(), expectedBoundaries.size() + 1);
1298+
for (size_t i = 0; i < expectedBoundaries.size(); ++i) {
1299+
const auto& partition = pathDescr.GetTablePartitions(i);
1300+
const auto actualBoundary = TSplitBoundarySerializer<T>::Deserialize(partition.GetEndOfRangeKeyPrefix()).at(0);
1301+
UNIT_ASSERT_VALUES_EQUAL_C(
1302+
actualBoundary, expectedBoundaries[i],
1303+
"partition index: " << i << '\n'
1304+
<< "actual key prefix: " << partition.GetEndOfRangeKeyPrefix().Quote() << '\n'
1305+
);
1306+
}
1307+
};
1308+
}
1309+
1310+
template TCheckFunc SplitBoundaries<ui32>(TVector<ui32>&&);
1311+
12601312
TCheckFunc ServerlessComputeResourcesMode(NKikimrSubDomains::EServerlessComputeResourcesMode serverlessComputeResourcesMode) {
12611313
return [=] (const NKikimrScheme::TEvDescribeSchemeResult& record) {
12621314
UNIT_ASSERT_C(IsGoodDomainStatus(record.GetStatus()), "Unexpected status: " << record.GetStatus());

ydb/core/tx/schemeshard/ut_helpers/ls_checks.h

+4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ namespace NLs {
9999
void CheckBoundaries(const NKikimrScheme::TEvDescribeSchemeResult& record);
100100
TCheckFunc PartitionCount(ui32 count);
101101
TCheckFunc PartitionKeys(TVector<TString> lastShardKeys);
102+
// Checks if the serialized representation of an expected boundary is a prefix of the actual one.
103+
// Similar to PartitionKeys check, but does not require you to pass split boundaries in a serialized form.
104+
template <typename T>
105+
TCheckFunc SplitBoundaries(TVector<T>&& expectedBoundaries);
102106
TCheckFunc FollowerCount(ui32 count);
103107
TCheckFunc CrossDataCenterFollowerCount(ui32 count);
104108
TCheckFunc AllowFollowerPromotion(bool val);

ydb/core/tx/schemeshard/ut_index_build/ut_index_build.cpp

+72
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,78 @@ Y_UNIT_TEST_SUITE(IndexBuildTest) {
910910
}
911911
}
912912

913+
Y_UNIT_TEST(IndexPartitioningIsPersisted) {
914+
TTestBasicRuntime runtime;
915+
TTestEnv env(runtime);
916+
ui64 txId = 100;
917+
918+
TestCreateTable(runtime, ++txId, "/MyRoot", R"(
919+
Name: "Table"
920+
Columns { Name: "key" Type: "Uint64" }
921+
Columns { Name: "value" Type: "Utf8" }
922+
KeyColumnNames: [ "key" ]
923+
)");
924+
env.TestWaitNotification(runtime, txId);
925+
926+
Ydb::Table::GlobalIndexSettings settings;
927+
UNIT_ASSERT(google::protobuf::TextFormat::ParseFromString(R"(
928+
partition_at_keys {
929+
split_points {
930+
type { tuple_type { elements { optional_type { item { type_id: UTF8 } } } } }
931+
value { items { text_value: "alice" } }
932+
}
933+
split_points {
934+
type { tuple_type { elements { optional_type { item { type_id: UTF8 } } } } }
935+
value { items { text_value: "bob" } }
936+
}
937+
}
938+
partitioning_settings {
939+
min_partitions_count: 3
940+
max_partitions_count: 3
941+
}
942+
)", &settings));
943+
944+
TBlockEvents<TEvSchemeShard::TEvModifySchemeTransaction> indexCreationBlocker(runtime, [](const auto& ev) {
945+
const auto& modifyScheme = ev->Get()->Record.GetTransaction(0);
946+
return modifyScheme.GetOperationType() == NKikimrSchemeOp::ESchemeOpCreateIndexBuild;
947+
});
948+
949+
const ui64 buildIndexTx = ++txId;
950+
TestBuildIndex(runtime, buildIndexTx, TTestTxConfig::SchemeShard, "/MyRoot", "/MyRoot/Table", TBuildIndexConfig{
951+
"Index", NKikimrSchemeOp::EIndexTypeGlobal, { "value" }, {},
952+
{ NYdb::NTable::TGlobalIndexSettings::FromProto(settings) }
953+
});
954+
955+
RebootTablet(runtime, TTestTxConfig::SchemeShard, runtime.AllocateEdgeActor());
956+
957+
indexCreationBlocker.Stop().Unblock();
958+
env.TestWaitNotification(runtime, buildIndexTx);
959+
960+
auto buildIndexOperation = TestGetBuildIndex(runtime, TTestTxConfig::SchemeShard, "/MyRoot", buildIndexTx);
961+
UNIT_ASSERT_VALUES_EQUAL_C(
962+
(int)buildIndexOperation.GetIndexBuild().GetState(), (int)Ydb::Table::IndexBuildState::STATE_DONE,
963+
buildIndexOperation.DebugString()
964+
);
965+
966+
TestDescribeResult(DescribePath(runtime, "/MyRoot/Table"), {
967+
NLs::IsTable,
968+
NLs::IndexesCount(1)
969+
});
970+
971+
TestDescribeResult(DescribePrivatePath(runtime, "/MyRoot/Table/Index"), {
972+
NLs::PathExist,
973+
NLs::IndexState(NKikimrSchemeOp::EIndexState::EIndexStateReady)
974+
});
975+
976+
TestDescribeResult(DescribePrivatePath(runtime, "/MyRoot/Table/Index/indexImplTable", true, true), {
977+
NLs::IsTable,
978+
NLs::PartitionCount(3),
979+
NLs::MinPartitionsCountEqual(3),
980+
NLs::MaxPartitionsCountEqual(3),
981+
NLs::PartitionKeys({"alice", "bob", ""})
982+
});
983+
}
984+
913985
Y_UNIT_TEST(DropIndex) {
914986
TTestBasicRuntime runtime;
915987
TTestEnv env(runtime);

ydb/core/tx/schemeshard/ut_index_build/ya.make

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ PEERDIR(
1414
ydb/core/testlib/default
1515
ydb/core/tx
1616
ydb/core/tx/schemeshard/ut_helpers
17+
ydb/public/sdk/cpp/client/ydb_table
1718
)
1819

1920
YQL_LAST_ABI_VERSION()

ydb/core/tx/schemeshard/ut_index_build_reboots/ut_index_build_reboots.cpp

+71
Original file line numberDiff line numberDiff line change
@@ -450,4 +450,75 @@ Y_UNIT_TEST_SUITE(IndexBuildTestReboots) {
450450

451451
});
452452
}
453+
454+
Y_UNIT_TEST(IndexPartitioning) {
455+
TTestWithReboots t(false);
456+
t.Run([&](TTestActorRuntime& runtime, bool& activeZone) {
457+
{
458+
TInactiveZone inactive(activeZone);
459+
460+
TestCreateTable(runtime, ++t.TxId, "/MyRoot", R"(
461+
Name: "Table"
462+
Columns { Name: "key" Type: "Uint32" }
463+
Columns { Name: "value" Type: "Utf8" }
464+
KeyColumnNames: [ "key" ]
465+
)");
466+
t.TestEnv->TestWaitNotification(runtime, t.TxId);
467+
}
468+
469+
Ydb::Table::GlobalIndexSettings settings;
470+
UNIT_ASSERT(google::protobuf::TextFormat::ParseFromString(R"(
471+
partition_at_keys {
472+
split_points {
473+
type { tuple_type { elements { optional_type { item { type_id: UTF8 } } } } }
474+
value { items { text_value: "alice" } }
475+
}
476+
split_points {
477+
type { tuple_type { elements { optional_type { item { type_id: UTF8 } } } } }
478+
value { items { text_value: "bob" } }
479+
}
480+
}
481+
partitioning_settings {
482+
min_partitions_count: 3
483+
max_partitions_count: 3
484+
}
485+
)", &settings));
486+
487+
const ui64 buildIndexId = ++t.TxId;
488+
AsyncBuildIndex(runtime, buildIndexId, TTestTxConfig::SchemeShard, "/MyRoot", "/MyRoot/Table", TBuildIndexConfig{
489+
"Index", NKikimrSchemeOp::EIndexTypeGlobal, { "value" }, {},
490+
{ NYdb::NTable::TGlobalIndexSettings::FromProto(settings) }
491+
});
492+
493+
{
494+
auto descr = TestGetBuildIndex(runtime, TTestTxConfig::SchemeShard, "/MyRoot", buildIndexId);
495+
UNIT_ASSERT_VALUES_EQUAL((int)descr.GetIndexBuild().GetState(), (int)Ydb::Table::IndexBuildState::STATE_PREPARING);
496+
}
497+
498+
t.TestEnv->TestWaitNotification(runtime, buildIndexId);
499+
500+
{
501+
auto descr = TestGetBuildIndex(runtime, TTestTxConfig::SchemeShard, "/MyRoot", buildIndexId);
502+
UNIT_ASSERT_VALUES_EQUAL((int)descr.GetIndexBuild().GetState(), (int)Ydb::Table::IndexBuildState::STATE_DONE);
503+
}
504+
505+
TestDescribeResult(DescribePath(runtime, "/MyRoot/Table"), {
506+
NLs::IsTable,
507+
NLs::IndexesCount(1)
508+
});
509+
510+
TestDescribeResult(DescribePrivatePath(runtime, "/MyRoot/Table/Index"), {
511+
NLs::PathExist,
512+
NLs::IndexState(NKikimrSchemeOp::EIndexState::EIndexStateReady)
513+
});
514+
515+
TestDescribeResult(DescribePrivatePath(runtime, "/MyRoot/Table/Index/indexImplTable", true, true), {
516+
NLs::IsTable,
517+
NLs::PartitionCount(3),
518+
NLs::MinPartitionsCountEqual(3),
519+
NLs::MaxPartitionsCountEqual(3),
520+
NLs::PartitionKeys({"alice", "bob", ""})
521+
});
522+
});
523+
}
453524
}

ydb/core/tx/schemeshard/ut_index_build_reboots/ya.make

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ PEERDIR(
1818
ydb/core/tx
1919
ydb/core/tx/schemeshard/ut_helpers
2020
ydb/library/yql/public/udf/service/exception_policy
21+
ydb/public/sdk/cpp/client/ydb_table
2122
)
2223

2324
SRCS(

ydb/public/sdk/cpp/client/ydb_table/table.cpp

+5-7
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,7 @@ static void SerializeTo(const TRenameIndex& rename, Ydb::Table::RenameIndexItem&
251251
proto.set_replace_destination(rename.ReplaceDestination_);
252252
}
253253

254-
template <typename TProto>
255-
TExplicitPartitions TExplicitPartitions::FromProto(const TProto& proto) {
254+
TExplicitPartitions TExplicitPartitions::FromProto(const Ydb::Table::ExplicitPartitions& proto) {
256255
TExplicitPartitions out;
257256
for (const auto& splitPoint : proto.split_points()) {
258257
TValue value(TType(splitPoint.type()), splitPoint.value());
@@ -2307,13 +2306,12 @@ ui64 TIndexDescription::GetSizeBytes() const {
23072306
return SizeBytes;
23082307
}
23092308

2310-
template <typename TProto>
2311-
TGlobalIndexSettings TGlobalIndexSettings::FromProto(const TProto& proto) {
2312-
auto partitionsFromProto = [](const auto& proto) -> TUniformOrExplicitPartitions {
2309+
TGlobalIndexSettings TGlobalIndexSettings::FromProto(const Ydb::Table::GlobalIndexSettings& proto) {
2310+
auto partitionsFromProto = [](const Ydb::Table::GlobalIndexSettings& proto) -> TUniformOrExplicitPartitions {
23132311
switch (proto.partitions_case()) {
2314-
case TProto::kUniformPartitions:
2312+
case Ydb::Table::GlobalIndexSettings::kUniformPartitions:
23152313
return proto.uniform_partitions();
2316-
case TProto::kPartitionAtKeys:
2314+
case Ydb::Table::GlobalIndexSettings::kPartitionAtKeys:
23172315
return TExplicitPartitions::FromProto(proto.partition_at_keys());
23182316
default:
23192317
return {};

ydb/public/sdk/cpp/client/ydb_table/table.h

+2-6
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,7 @@ struct TExplicitPartitions {
184184

185185
FLUENT_SETTING_VECTOR(TValue, SplitPoints);
186186

187-
template <typename TProto>
188-
static TExplicitPartitions FromProto(const TProto& proto);
189-
187+
static TExplicitPartitions FromProto(const Ydb::Table::ExplicitPartitions& proto);
190188
void SerializeTo(Ydb::Table::ExplicitPartitions& proto) const;
191189
};
192190

@@ -196,9 +194,7 @@ struct TGlobalIndexSettings {
196194
TPartitioningSettings PartitioningSettings;
197195
TUniformOrExplicitPartitions Partitions;
198196

199-
template <typename TProto>
200-
static TGlobalIndexSettings FromProto(const TProto& proto);
201-
197+
static TGlobalIndexSettings FromProto(const Ydb::Table::GlobalIndexSettings& proto);
202198
void SerializeTo(Ydb::Table::GlobalIndexSettings& proto) const;
203199
};
204200

ydb/tests/functional/scheme_tests/canondata/tablet_scheme_tests.TestTabletSchemes.test_tablet_schemes_flat_schemeshard_/flat_schemeshard.schema

+7-1
Original file line numberDiff line numberDiff line change
@@ -4788,6 +4788,11 @@
47884788
"ColumnId": 33,
47894789
"ColumnName": "AlterMainTableTxDone",
47904790
"ColumnType": "Bool"
4791+
},
4792+
{
4793+
"ColumnId": 34,
4794+
"ColumnName": "CreationConfig",
4795+
"ColumnType": "String"
47914796
}
47924797
],
47934798
"ColumnsDropped": [],
@@ -4826,7 +4831,8 @@
48264831
30,
48274832
31,
48284833
32,
4829-
33
4834+
33,
4835+
34
48304836
],
48314837
"RoomID": 0,
48324838
"Codec": 0,

0 commit comments

Comments
 (0)