Skip to content

24-3: Index build: do not lose the requested partitioning settings of an indexImplTable in case of SchemeShard reboots (#10579) #10634

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion ydb/core/tx/schemeshard/schemeshard_build_index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ void TSchemeShard::Handle(TEvPrivate::TEvIndexBuildingMakeABill::TPtr& ev, const

void TSchemeShard::PersistCreateBuildIndex(NIceDb::TNiceDb& db, const TIndexBuildInfo::TPtr info) {
Y_ABORT_UNLESS(info->BuildKind != TIndexBuildInfo::EBuildKind::BuildKindUnspecified);
db.Table<Schema::IndexBuild>().Key(info->Id).Update(
auto persistedBuildIndex = db.Table<Schema::IndexBuild>().Key(info->Id);
persistedBuildIndex.Update(
NIceDb::TUpdate<Schema::IndexBuild::Uid>(info->Uid),
NIceDb::TUpdate<Schema::IndexBuild::DomainOwnerId>(info->DomainPathId.OwnerId),
NIceDb::TUpdate<Schema::IndexBuild::DomainLocalId>(info->DomainPathId.LocalPathId),
Expand All @@ -48,6 +49,17 @@ void TSchemeShard::PersistCreateBuildIndex(NIceDb::TNiceDb& db, const TIndexBuil
NIceDb::TUpdate<Schema::IndexBuild::MaxRetries>(info->Limits.MaxRetries),
NIceDb::TUpdate<Schema::IndexBuild::BuildKind>(ui32(info->BuildKind))
);
// Persist details of the index build operation: ImplTableDescription.
// We have chosen TIndexCreationConfig's string representation as the serialization format.
{
NKikimrSchemeOp::TIndexCreationConfig serializableRepresentation;

*serializableRepresentation.MutableIndexImplTableDescription() = info->ImplTableDescription;

persistedBuildIndex.Update(
NIceDb::TUpdate<Schema::IndexBuild::CreationConfig>(serializableRepresentation.SerializeAsString())
);
}

ui32 columnNo = 0;
for (ui32 i = 0; i < info->IndexColumns.size(); ++i, ++columnNo) {
Expand Down
8 changes: 8 additions & 0 deletions ydb/core/tx/schemeshard/schemeshard_info_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -3092,6 +3092,14 @@ struct TIndexBuildInfo: public TSimpleRefCount<TIndexBuildInfo> {
indexInfo->IndexName = row.template GetValue<Schema::IndexBuild::IndexName>();
indexInfo->IndexType = row.template GetValue<Schema::IndexBuild::IndexType>();

// Restore the operation details: ImplTableDescription.
if (row.template HaveValue<Schema::IndexBuild::CreationConfig>()) {
NKikimrSchemeOp::TIndexCreationConfig creationConfig;
Y_ABORT_UNLESS(creationConfig.ParseFromString(row.template GetValue<Schema::IndexBuild::CreationConfig>()));

indexInfo->ImplTableDescription = std::move(*creationConfig.MutableIndexImplTableDescription());
}

indexInfo->State = TIndexBuildInfo::EState(
row.template GetValue<Schema::IndexBuild::State>());
indexInfo->Issue =
Expand Down
6 changes: 5 additions & 1 deletion ydb/core/tx/schemeshard/schemeshard_schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1318,6 +1318,9 @@ struct Schema : NIceDb::Schema {
struct AlterMainTableTxStatus : Column<32, NScheme::NTypeIds::Uint32> { using Type = NKikimrScheme::EStatus; };
struct AlterMainTableTxDone : Column<33, NScheme::NTypeIds::Bool> {};

// Serialized as string NKikimrSchemeOp::TIndexCreationConfig protobuf.
struct CreationConfig : Column<34, NScheme::NTypeIds::String> { using Type = TString; };

using TKey = TableKey<Id>;
using TColumns = TableColumns<
Id,
Expand Down Expand Up @@ -1352,7 +1355,8 @@ struct Schema : NIceDb::Schema {
BuildKind,
AlterMainTableTxId,
AlterMainTableTxStatus,
AlterMainTableTxDone
AlterMainTableTxDone,
CreationConfig
>;
};

Expand Down
54 changes: 53 additions & 1 deletion ydb/core/tx/schemeshard/ut_helpers/ls_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1252,11 +1252,63 @@ TCheckFunc PartitionKeys(TVector<TString> lastShardKeys) {
const auto& pathDescr = record.GetPathDescription();
UNIT_ASSERT_VALUES_EQUAL(lastShardKeys.size(), pathDescr.TablePartitionsSize());
for (size_t i = 0; i < lastShardKeys.size(); ++i) {
UNIT_ASSERT_STRING_CONTAINS(pathDescr.GetTablePartitions(i).GetEndOfRangeKeyPrefix(), lastShardKeys[i]);
const auto& partition = pathDescr.GetTablePartitions(i);
UNIT_ASSERT_STRING_CONTAINS_C(
partition.GetEndOfRangeKeyPrefix(), lastShardKeys[i],
"partition index: " << i << '\n'
<< "actual key prefix: " << partition.GetEndOfRangeKeyPrefix().Quote() << '\n'
<< "expected key prefix: " << lastShardKeys[i].Quote() << '\n'
);
}
};
}

namespace {

// Serializes / deserializes a value of type T to a cell vector string representation.
template <typename T>
struct TSplitBoundarySerializer {
static TString Serialize(T splitBoundary) {
const auto cell = TCell::Make(splitBoundary);
TSerializedCellVec cellVec(TArrayRef<const TCell>(&cell, 1));
return cellVec.ReleaseBuffer();
}

static TVector<T> Deserialize(const TString& serializedCells) {
TSerializedCellVec cells(serializedCells);
TVector<T> values;
for (const auto& cell : cells.GetCells()) {
if (cell.IsNull()) {
// the last cell
break;
}
values.emplace_back(cell.AsValue<T>());
}
return values;
}
};

}

template <typename T>
TCheckFunc SplitBoundaries(TVector<T>&& expectedBoundaries) {
return [expectedBoundaries = std::move(expectedBoundaries)] (const NKikimrScheme::TEvDescribeSchemeResult& record) {
const auto& pathDescr = record.GetPathDescription();
UNIT_ASSERT_VALUES_EQUAL(pathDescr.TablePartitionsSize(), expectedBoundaries.size() + 1);
for (size_t i = 0; i < expectedBoundaries.size(); ++i) {
const auto& partition = pathDescr.GetTablePartitions(i);
const auto actualBoundary = TSplitBoundarySerializer<T>::Deserialize(partition.GetEndOfRangeKeyPrefix()).at(0);
UNIT_ASSERT_VALUES_EQUAL_C(
actualBoundary, expectedBoundaries[i],
"partition index: " << i << '\n'
<< "actual key prefix: " << partition.GetEndOfRangeKeyPrefix().Quote() << '\n'
);
}
};
}

template TCheckFunc SplitBoundaries<ui32>(TVector<ui32>&&);

TCheckFunc ServerlessComputeResourcesMode(NKikimrSubDomains::EServerlessComputeResourcesMode serverlessComputeResourcesMode) {
return [=] (const NKikimrScheme::TEvDescribeSchemeResult& record) {
UNIT_ASSERT_C(IsGoodDomainStatus(record.GetStatus()), "Unexpected status: " << record.GetStatus());
Expand Down
4 changes: 4 additions & 0 deletions ydb/core/tx/schemeshard/ut_helpers/ls_checks.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ namespace NLs {
void CheckBoundaries(const NKikimrScheme::TEvDescribeSchemeResult& record);
TCheckFunc PartitionCount(ui32 count);
TCheckFunc PartitionKeys(TVector<TString> lastShardKeys);
// Checks if the serialized representation of an expected boundary is a prefix of the actual one.
// Similar to PartitionKeys check, but does not require you to pass split boundaries in a serialized form.
template <typename T>
TCheckFunc SplitBoundaries(TVector<T>&& expectedBoundaries);
TCheckFunc FollowerCount(ui32 count);
TCheckFunc CrossDataCenterFollowerCount(ui32 count);
TCheckFunc AllowFollowerPromotion(bool val);
Expand Down
72 changes: 72 additions & 0 deletions ydb/core/tx/schemeshard/ut_index_build/ut_index_build.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,78 @@ Y_UNIT_TEST_SUITE(IndexBuildTest) {
}
}

Y_UNIT_TEST(IndexPartitioningIsPersisted) {
TTestBasicRuntime runtime;
TTestEnv env(runtime);
ui64 txId = 100;

TestCreateTable(runtime, ++txId, "/MyRoot", R"(
Name: "Table"
Columns { Name: "key" Type: "Uint64" }
Columns { Name: "value" Type: "Utf8" }
KeyColumnNames: [ "key" ]
)");
env.TestWaitNotification(runtime, txId);

Ydb::Table::GlobalIndexSettings settings;
UNIT_ASSERT(google::protobuf::TextFormat::ParseFromString(R"(
partition_at_keys {
split_points {
type { tuple_type { elements { optional_type { item { type_id: UTF8 } } } } }
value { items { text_value: "alice" } }
}
split_points {
type { tuple_type { elements { optional_type { item { type_id: UTF8 } } } } }
value { items { text_value: "bob" } }
}
}
partitioning_settings {
min_partitions_count: 3
max_partitions_count: 3
}
)", &settings));

TBlockEvents<TEvSchemeShard::TEvModifySchemeTransaction> indexCreationBlocker(runtime, [](const auto& ev) {
const auto& modifyScheme = ev->Get()->Record.GetTransaction(0);
return modifyScheme.GetOperationType() == NKikimrSchemeOp::ESchemeOpCreateIndexBuild;
});

const ui64 buildIndexTx = ++txId;
TestBuildIndex(runtime, buildIndexTx, TTestTxConfig::SchemeShard, "/MyRoot", "/MyRoot/Table", TBuildIndexConfig{
"Index", NKikimrSchemeOp::EIndexTypeGlobal, { "value" }, {},
{ NYdb::NTable::TGlobalIndexSettings::FromProto(settings) }
});

RebootTablet(runtime, TTestTxConfig::SchemeShard, runtime.AllocateEdgeActor());

indexCreationBlocker.Stop().Unblock();
env.TestWaitNotification(runtime, buildIndexTx);

auto buildIndexOperation = TestGetBuildIndex(runtime, TTestTxConfig::SchemeShard, "/MyRoot", buildIndexTx);
UNIT_ASSERT_VALUES_EQUAL_C(
(int)buildIndexOperation.GetIndexBuild().GetState(), (int)Ydb::Table::IndexBuildState::STATE_DONE,
buildIndexOperation.DebugString()
);

TestDescribeResult(DescribePath(runtime, "/MyRoot/Table"), {
NLs::IsTable,
NLs::IndexesCount(1)
});

TestDescribeResult(DescribePrivatePath(runtime, "/MyRoot/Table/Index"), {
NLs::PathExist,
NLs::IndexState(NKikimrSchemeOp::EIndexState::EIndexStateReady)
});

TestDescribeResult(DescribePrivatePath(runtime, "/MyRoot/Table/Index/indexImplTable", true, true), {
NLs::IsTable,
NLs::PartitionCount(3),
NLs::MinPartitionsCountEqual(3),
NLs::MaxPartitionsCountEqual(3),
NLs::PartitionKeys({"alice", "bob", ""})
});
}

Y_UNIT_TEST(DropIndex) {
TTestBasicRuntime runtime;
TTestEnv env(runtime);
Expand Down
1 change: 1 addition & 0 deletions ydb/core/tx/schemeshard/ut_index_build/ya.make
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ PEERDIR(
ydb/core/testlib/default
ydb/core/tx
ydb/core/tx/schemeshard/ut_helpers
ydb/public/sdk/cpp/client/ydb_table
)

YQL_LAST_ABI_VERSION()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,4 +450,75 @@ Y_UNIT_TEST_SUITE(IndexBuildTestReboots) {

});
}

Y_UNIT_TEST(IndexPartitioning) {
TTestWithReboots t(false);
t.Run([&](TTestActorRuntime& runtime, bool& activeZone) {
{
TInactiveZone inactive(activeZone);

TestCreateTable(runtime, ++t.TxId, "/MyRoot", R"(
Name: "Table"
Columns { Name: "key" Type: "Uint32" }
Columns { Name: "value" Type: "Utf8" }
KeyColumnNames: [ "key" ]
)");
t.TestEnv->TestWaitNotification(runtime, t.TxId);
}

Ydb::Table::GlobalIndexSettings settings;
UNIT_ASSERT(google::protobuf::TextFormat::ParseFromString(R"(
partition_at_keys {
split_points {
type { tuple_type { elements { optional_type { item { type_id: UTF8 } } } } }
value { items { text_value: "alice" } }
}
split_points {
type { tuple_type { elements { optional_type { item { type_id: UTF8 } } } } }
value { items { text_value: "bob" } }
}
}
partitioning_settings {
min_partitions_count: 3
max_partitions_count: 3
}
)", &settings));

const ui64 buildIndexId = ++t.TxId;
AsyncBuildIndex(runtime, buildIndexId, TTestTxConfig::SchemeShard, "/MyRoot", "/MyRoot/Table", TBuildIndexConfig{
"Index", NKikimrSchemeOp::EIndexTypeGlobal, { "value" }, {},
{ NYdb::NTable::TGlobalIndexSettings::FromProto(settings) }
});

{
auto descr = TestGetBuildIndex(runtime, TTestTxConfig::SchemeShard, "/MyRoot", buildIndexId);
UNIT_ASSERT_VALUES_EQUAL((int)descr.GetIndexBuild().GetState(), (int)Ydb::Table::IndexBuildState::STATE_PREPARING);
}

t.TestEnv->TestWaitNotification(runtime, buildIndexId);

{
auto descr = TestGetBuildIndex(runtime, TTestTxConfig::SchemeShard, "/MyRoot", buildIndexId);
UNIT_ASSERT_VALUES_EQUAL((int)descr.GetIndexBuild().GetState(), (int)Ydb::Table::IndexBuildState::STATE_DONE);
}

TestDescribeResult(DescribePath(runtime, "/MyRoot/Table"), {
NLs::IsTable,
NLs::IndexesCount(1)
});

TestDescribeResult(DescribePrivatePath(runtime, "/MyRoot/Table/Index"), {
NLs::PathExist,
NLs::IndexState(NKikimrSchemeOp::EIndexState::EIndexStateReady)
});

TestDescribeResult(DescribePrivatePath(runtime, "/MyRoot/Table/Index/indexImplTable", true, true), {
NLs::IsTable,
NLs::PartitionCount(3),
NLs::MinPartitionsCountEqual(3),
NLs::MaxPartitionsCountEqual(3),
NLs::PartitionKeys({"alice", "bob", ""})
});
});
}
}
1 change: 1 addition & 0 deletions ydb/core/tx/schemeshard/ut_index_build_reboots/ya.make
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ PEERDIR(
ydb/core/tx
ydb/core/tx/schemeshard/ut_helpers
ydb/library/yql/public/udf/service/exception_policy
ydb/public/sdk/cpp/client/ydb_table
)

SRCS(
Expand Down
12 changes: 5 additions & 7 deletions ydb/public/sdk/cpp/client/ydb_table/table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,7 @@ static void SerializeTo(const TRenameIndex& rename, Ydb::Table::RenameIndexItem&
proto.set_replace_destination(rename.ReplaceDestination_);
}

template <typename TProto>
TExplicitPartitions TExplicitPartitions::FromProto(const TProto& proto) {
TExplicitPartitions TExplicitPartitions::FromProto(const Ydb::Table::ExplicitPartitions& proto) {
TExplicitPartitions out;
for (const auto& splitPoint : proto.split_points()) {
TValue value(TType(splitPoint.type()), splitPoint.value());
Expand Down Expand Up @@ -2307,13 +2306,12 @@ ui64 TIndexDescription::GetSizeBytes() const {
return SizeBytes;
}

template <typename TProto>
TGlobalIndexSettings TGlobalIndexSettings::FromProto(const TProto& proto) {
auto partitionsFromProto = [](const auto& proto) -> TUniformOrExplicitPartitions {
TGlobalIndexSettings TGlobalIndexSettings::FromProto(const Ydb::Table::GlobalIndexSettings& proto) {
auto partitionsFromProto = [](const Ydb::Table::GlobalIndexSettings& proto) -> TUniformOrExplicitPartitions {
switch (proto.partitions_case()) {
case TProto::kUniformPartitions:
case Ydb::Table::GlobalIndexSettings::kUniformPartitions:
return proto.uniform_partitions();
case TProto::kPartitionAtKeys:
case Ydb::Table::GlobalIndexSettings::kPartitionAtKeys:
return TExplicitPartitions::FromProto(proto.partition_at_keys());
default:
return {};
Expand Down
8 changes: 2 additions & 6 deletions ydb/public/sdk/cpp/client/ydb_table/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,7 @@ struct TExplicitPartitions {

FLUENT_SETTING_VECTOR(TValue, SplitPoints);

template <typename TProto>
static TExplicitPartitions FromProto(const TProto& proto);

static TExplicitPartitions FromProto(const Ydb::Table::ExplicitPartitions& proto);
void SerializeTo(Ydb::Table::ExplicitPartitions& proto) const;
};

Expand All @@ -196,9 +194,7 @@ struct TGlobalIndexSettings {
TPartitioningSettings PartitioningSettings;
TUniformOrExplicitPartitions Partitions;

template <typename TProto>
static TGlobalIndexSettings FromProto(const TProto& proto);

static TGlobalIndexSettings FromProto(const Ydb::Table::GlobalIndexSettings& proto);
void SerializeTo(Ydb::Table::GlobalIndexSettings& proto) const;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4788,6 +4788,11 @@
"ColumnId": 33,
"ColumnName": "AlterMainTableTxDone",
"ColumnType": "Bool"
},
{
"ColumnId": 34,
"ColumnName": "CreationConfig",
"ColumnType": "String"
}
],
"ColumnsDropped": [],
Expand Down Expand Up @@ -4826,7 +4831,8 @@
30,
31,
32,
33
33,
34
],
"RoomID": 0,
"Codec": 0,
Expand Down
Loading