diff options
| author | ijon <[email protected]> | 2024-10-21 18:14:36 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-10-21 18:14:36 +0300 |
| commit | ff01ea7d10c8495ae6ce28097ff63d13760ae785 (patch) | |
| tree | 348220cb2b1a175865c40db2dd75003775923150 | |
| parent | 989a4fa04b3fdab90e58f07f7f995cc936cc6763 (diff) | |
schemeshard: split schemeshard__operation_common.cpp, move code from .h (#10631)
Move operations specific content of schemeshard__operation_common.cpp (bsv, cdc_stream, pq, subdomain) into separate files.
Move dependency rich methods definitions out of schemeshard__operation_common.h.
This is part of "improve schemeshard operations build time" effort (#10633).
8 files changed, 2117 insertions, 1990 deletions
diff --git a/ydb/core/tx/schemeshard/schemeshard__operation_common.cpp b/ydb/core/tx/schemeshard/schemeshard__operation_common.cpp index 19fe974c4b6..c6260ddbd40 100644 --- a/ydb/core/tx/schemeshard/schemeshard__operation_common.cpp +++ b/ydb/core/tx/schemeshard/schemeshard__operation_common.cpp @@ -82,9 +82,393 @@ THolder<TEvHive::TEvCreateTablet> CreateEvCreateTablet(TPathElement::TPtr target return ev; } +// TCreateParts +// +TCreateParts::TCreateParts(const TOperationId& id) + : OperationId(id) +{ + IgnoreMessages(DebugHint(), {}); +} + +bool TCreateParts::HandleReply(TEvHive::TEvAdoptTabletReply::TPtr& ev, TOperationContext& context) { + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvAdoptTablet" + << ", at tabletId: " << context.SS->SelfTabletId()); + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvAdoptTablet" + << ", message% " << DebugReply(ev)); + + NIceDb::TNiceDb db(context.GetDB()); + + const TString& explain = ev->Get()->Record.GetExplain(); + TTabletId tabletId = TTabletId(ev->Get()->Record.GetTabletID()); // global id from hive + auto shardIdx = context.SS->MakeLocalId(TLocalShardIdx(ev->Get()->Record.GetOwnerIdx())); // global id from hive + TTabletId hive = TTabletId(ev->Get()->Record.GetOrigin()); + + Y_ABORT_UNLESS(ui64(context.SS->SelfTabletId()) == ev->Get()->Record.GetOwner()); + + NKikimrProto::EReplyStatus status = ev->Get()->Record.GetStatus(); + Y_VERIFY_S(status == NKikimrProto::OK || status == NKikimrProto::ALREADY, + "Unexpected status " << NKikimrProto::EReplyStatus_Name(status) + << " in AdoptTabletReply for tabletId " << tabletId + << " with explain " << explain); + + // Note that HIVE might send duplicate TTxAdoptTabletReply in case of restarts + // So we just ignore the event if we cannot find the Tx or if it is in a different + // state + + Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shardIdx)); + + if (!context.SS->AdoptedShards.contains(shardIdx)) { + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvAdoptTablet" + << " Got TTxAdoptTabletReply for shard but it is not present in AdoptedShards" + << ", shardIdx: " << shardIdx + << ", tabletId:" << tabletId); + return false; + } + + TShardInfo& shardInfo = context.SS->ShardInfos[shardIdx]; + Y_ABORT_UNLESS(shardInfo.TabletID == InvalidTabletId || shardInfo.TabletID == tabletId); + + Y_ABORT_UNLESS(tabletId != InvalidTabletId); + shardInfo.TabletID = tabletId; + context.SS->TabletIdToShardIdx[tabletId] = shardIdx; + + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(txState->State == TTxState::CreateParts); + + txState->ShardsInProgress.erase(shardIdx); + + context.SS->AdoptedShards.erase(shardIdx); + context.SS->PersistDeleteAdopted(db, shardIdx); + context.SS->PersistShardMapping(db, shardIdx, tabletId, shardInfo.PathId, OperationId.GetTxId(), shardInfo.TabletType); + + context.OnComplete.UnbindMsgFromPipe(OperationId, hive, shardIdx); + context.OnComplete.ActivateShardCreated(shardIdx, OperationId.GetTxId()); + + // If all datashards have been created + if (txState->ShardsInProgress.empty()) { + context.SS->ChangeTxState(db, OperationId, TTxState::ConfigureParts); + return true; + } + + return false; +} + +bool TCreateParts::HandleReply(TEvHive::TEvCreateTabletReply::TPtr& ev, TOperationContext& context) { + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvCreateTabletReply" + << ", at tabletId: " << context.SS->SelfTabletId()); + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvCreateTabletReply" + << ", message: " << DebugReply(ev)); + + NIceDb::TNiceDb db(context.GetDB()); + + auto shardIdx = TShardIdx(ev->Get()->Record.GetOwner(), + TLocalShardIdx(ev->Get()->Record.GetOwnerIdx())); + + auto tabletId = TTabletId(ev->Get()->Record.GetTabletID()); // global id from hive + auto hive = TTabletId(ev->Get()->Record.GetOrigin()); + + NKikimrProto::EReplyStatus status = ev->Get()->Record.GetStatus(); + + Y_VERIFY_S(status == NKikimrProto::OK + || status == NKikimrProto::ALREADY + || status == NKikimrProto::INVALID_OWNER + || status == NKikimrProto::BLOCKED, + "Unexpected status " << NKikimrProto::EReplyStatus_Name(status) + << " in CreateTabletReply shard idx " << shardIdx << " tabletId " << tabletId); + + if (status == NKikimrProto::BLOCKED) { + Y_ABORT_UNLESS(!context.SS->IsDomainSchemeShard); + + LOG_NOTICE_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " CreateRequest BLOCKED " + << " at Hive: " << hive + << " msg: " << DebugReply(ev)); + + // do not unsubscribe message + // context.OnComplete.UnbindMsgFromPipe(OperationId, hive, shardIdx); + + // just stay calm and hung until tenant schemeshard is deleted + return false; + } + + TTxState& txState = *context.SS->FindTx(OperationId); + + TShardInfo& shardInfo = context.SS->ShardInfos.at(shardIdx); + Y_ABORT_UNLESS(shardInfo.TabletID == InvalidTabletId || shardInfo.TabletID == tabletId); + + if (status == NKikimrProto::INVALID_OWNER) { + auto redirectTo = TTabletId(ev->Get()->Record.GetForwardRequest().GetHiveTabletId()); + Y_ABORT_UNLESS(redirectTo); + Y_ABORT_UNLESS(tabletId); + + context.OnComplete.UnbindMsgFromPipe(OperationId, hive, shardIdx); + + auto path = context.SS->PathsById.at(txState.TargetPathId); + auto request = CreateEvCreateTablet(path, shardIdx, context); + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " CreateRequest" + << " Redirect from Hive: " << hive + << " to Hive: " << redirectTo + << " msg: " << request->Record.DebugString()); + + context.OnComplete.BindMsgToPipe(OperationId, redirectTo, shardIdx, request.Release()); + return false; + } + + if (shardInfo.TabletID == InvalidTabletId) { + switch (shardInfo.TabletType) { + case ETabletType::DataShard: + context.SS->TabletCounters->Simple()[COUNTER_TABLE_SHARD_ACTIVE_COUNT].Add(1); + break; + case ETabletType::Coordinator: + context.SS->TabletCounters->Simple()[COUNTER_SUB_DOMAIN_COORDINATOR_COUNT].Add(1); + break; + case ETabletType::Mediator: + context.SS->TabletCounters->Simple()[COUNTER_SUB_DOMAIN_MEDIATOR_COUNT].Add(1); + break; + case ETabletType::Hive: + context.SS->TabletCounters->Simple()[COUNTER_SUB_DOMAIN_HIVE_COUNT].Add(1); + break; + case ETabletType::SysViewProcessor: + context.SS->TabletCounters->Simple()[COUNTER_SYS_VIEW_PROCESSOR_COUNT].Add(1); + break; + case ETabletType::StatisticsAggregator: + context.SS->TabletCounters->Simple()[COUNTER_STATISTICS_AGGREGATOR_COUNT].Add(1); + break; + case ETabletType::BackupController: + context.SS->TabletCounters->Simple()[COUNTER_BACKUP_CONTROLLER_TABLET_COUNT].Add(1); + break; + default: + break; + } + } + + Y_ABORT_UNLESS(tabletId != InvalidTabletId); + shardInfo.TabletID = tabletId; + context.SS->TabletIdToShardIdx[tabletId] = shardIdx; + + Y_ABORT_UNLESS(OperationId.GetTxId() == shardInfo.CurrentTxId); + + txState.ShardsInProgress.erase(shardIdx); + + context.SS->PersistShardMapping(db, shardIdx, tabletId, shardInfo.PathId, OperationId.GetTxId(), shardInfo.TabletType); + context.OnComplete.UnbindMsgFromPipe(OperationId, hive, shardIdx); + context.OnComplete.ActivateShardCreated(shardIdx, OperationId.GetTxId()); + + // If all datashards have been created + if (txState.ShardsInProgress.empty()) { + context.SS->ChangeTxState(db, OperationId, TTxState::ConfigureParts); + return true; + } + + return false; +} + +THolder<TEvHive::TEvAdoptTablet> TCreateParts::AdoptRequest(TShardIdx shardIdx, TOperationContext& context) { + Y_ABORT_UNLESS(context.SS->AdoptedShards.contains(shardIdx)); + auto& adoptedShard = context.SS->AdoptedShards[shardIdx]; + auto& shard = context.SS->ShardInfos[shardIdx]; + + THolder<TEvHive::TEvAdoptTablet> ev = MakeHolder<TEvHive::TEvAdoptTablet>( + ui64(shard.TabletID), + adoptedShard.PrevOwner, ui64(adoptedShard.PrevShardIdx), + shard.TabletType, + ui64(context.SS->SelfTabletId()), ui64(shardIdx.GetLocalId())); + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " AdoptRequest" + << " Event to Hive: " << ev->Record.DebugString().c_str()); + + return ev; +} + +bool TCreateParts::ProgressState(TOperationContext& context) { + TTabletId ssId = context.SS->SelfTabletId(); + + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " ProgressState" + << ", operation type: " << TTxState::TypeName(txState->TxType) + << ", at tablet" << ssId); + + if (txState->TxType == TTxState::TxDropTable + || txState->TxType == TTxState::TxAlterTable + || txState->TxType == TTxState::TxBackup + || txState->TxType == TTxState::TxRestore) { + if (NTableState::CheckPartitioningChangedForTableModification(*txState, context)) { + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " ProgressState" + << " SourceTablePartitioningChangedForModification" + << ", tx type: " << TTxState::TypeName(txState->TxType)); + NTableState::UpdatePartitioningForTableModification(OperationId, *txState, context); + } + } else if (txState->TxType == TTxState::TxCopyTable) { + if (NTableState::SourceTablePartitioningChangedForCopyTable(*txState, context)) { + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " ProgressState" + << " SourceTablePartitioningChangedForCopyTable" + << ", tx type: " << TTxState::TypeName(txState->TxType)); + NTableState::UpdatePartitioningForCopyTable(OperationId, *txState, context); + } + } + + txState->ClearShardsInProgress(); + + bool nothingToDo = true; + for (const auto& shard : txState->Shards) { + if (shard.Operation != TTxState::CreateParts) { + continue; + } + nothingToDo = false; + + if (context.SS->AdoptedShards.contains(shard.Idx)) { + auto ev = AdoptRequest(shard.Idx, context); + context.OnComplete.BindMsgToPipe(OperationId, context.SS->GetGlobalHive(context.Ctx), shard.Idx, ev.Release()); + } else { + auto path = context.SS->PathsById.at(txState->TargetPathId); + auto ev = CreateEvCreateTablet(path, shard.Idx, context); + + auto hiveToRequest = context.SS->ResolveHive(shard.Idx, context.Ctx); + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " CreateRequest" + << " Event to Hive: " << hiveToRequest + << " msg: "<< ev->Record.DebugString().c_str()); + + context.OnComplete.BindMsgToPipe(OperationId, hiveToRequest, shard.Idx, ev.Release()); + } + context.OnComplete.RouteByShardIdx(OperationId, shard.Idx); + } + + if (nothingToDo) { + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " ProgressState" + << " no shards to create, do next state"); + + NIceDb::TNiceDb db(context.GetDB()); + context.SS->ChangeTxState(db, OperationId, TTxState::ConfigureParts); + return true; + } + + txState->UpdateShardsInProgress(TTxState::CreateParts); + return false; +} + +// TDeleteParts +// +TDeleteParts::TDeleteParts(const TOperationId& id, TTxState::ETxState nextState) + : OperationId(id) + , NextState(nextState) +{ + IgnoreMessages(DebugHint(), {}); +} + +void TDeleteParts::DeleteShards(TOperationContext& context) { + const auto* txState = context.SS->FindTx(OperationId); + + // Initiate asynchronous deletion of all shards + for (const auto& shard : txState->Shards) { + context.OnComplete.DeleteShard(shard.Idx); + } +} -namespace +bool TDeleteParts::ProgressState(TOperationContext& context) { + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "[" << context.SS->SelfTabletId() << "] " << DebugHint() << " ProgressState"); + DeleteShards(context); + + NIceDb::TNiceDb db(context.GetDB()); + context.SS->ChangeTxState(db, OperationId, NextState); + return true; +} + +// TDeletePartsAndDone +// +TDeletePartsAndDone::TDeletePartsAndDone(const TOperationId& id) + : TDeleteParts(id) { + Y_UNUSED(NextState); +} + +bool TDeletePartsAndDone::ProgressState(TOperationContext& context) { + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "[" << context.SS->SelfTabletId() << "] " << DebugHint() << " ProgressState"); + DeleteShards(context); + + context.OnComplete.DoneOperation(OperationId); + return true; +} + +// TDone +// +TDone::TDone(const TOperationId& id) + : OperationId(id) +{ + IgnoreMessages(DebugHint(), AllIncomingEvents()); +} + +bool TDone::ProgressState(TOperationContext& context) { + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "[" << context.SS->SelfTabletId() << "] " << DebugHint() << " ProgressState"); + + const auto* txState = context.SS->FindTx(OperationId); + + const auto& pathId = txState->TargetPathId; + Y_ABORT_UNLESS(context.SS->PathsById.contains(pathId)); + TPathElement::TPtr path = context.SS->PathsById.at(pathId); + Y_VERIFY_S(path->PathState != TPathElement::EPathState::EPathStateNoChanges, "with context" + << ", PathState: " << NKikimrSchemeOp::EPathState_Name(path->PathState) + << ", PathId: " << path->PathId + << ", PathName: " << path->Name); + + if (path->IsPQGroup() && txState->IsCreate()) { + TPathElement::TPtr parentDir = context.SS->PathsById.at(path->ParentPathId); + // can't be removed until KIKIMR-8861 + // at least lets wrap it into condition + // it helps to actualize PATHSTATE inside children listing + context.SS->ClearDescribePathCaches(parentDir); + } + + if (txState->IsDrop()) { + context.OnComplete.ReleasePathState(OperationId, path->PathId, TPathElement::EPathState::EPathStateNotExist); + } else { + context.OnComplete.ReleasePathState(OperationId, path->PathId, TPathElement::EPathState::EPathStateNoChanges); + } + + if (txState->SourcePathId != InvalidPathId) { + Y_ABORT_UNLESS(context.SS->PathsById.contains(txState->SourcePathId)); + TPathElement::TPtr srcPath = context.SS->PathsById.at(txState->SourcePathId); + if (srcPath->PathState == TPathElement::EPathState::EPathStateCopying) { + context.OnComplete.ReleasePathState(OperationId, srcPath->PathId, TPathElement::EPathState::EPathStateNoChanges); + } + } + + // OlapStore tracks all tables that are under operation, make sure to unlink + if (context.SS->ColumnTables.contains(pathId)) { + auto tableInfo = context.SS->ColumnTables.at(pathId); + if (!tableInfo->IsStandalone()) { + const auto storePathId = tableInfo->GetOlapStorePathIdVerified(); + if (context.SS->OlapStores.contains(storePathId)) { + auto storeInfo = context.SS->OlapStores.at(storePathId); + storeInfo->ColumnTablesUnderOperation.erase(pathId); + } + } + } + + context.OnComplete.DoneOperation(OperationId); + return true; +} + +namespace { template <typename T, typename TFuncCheck, typename TFuncToString> bool CollectProposeTxResults( @@ -158,7 +542,9 @@ bool CollectProposeTxResults( } // anonymous namespace -bool NTableState::CollectProposeTransactionResults( +namespace NTableState { + +bool CollectProposeTransactionResults( const NKikimr::NSchemeShard::TOperationId &operationId, const TEvDataShard::TEvProposeTransactionResult::TPtr &ev, NKikimr::NSchemeShard::TOperationContext &context) @@ -174,7 +560,7 @@ bool NTableState::CollectProposeTransactionResults( return CollectProposeTxResults(ev, operationId, context, prepared, toString); } -bool NTableState::CollectProposeTransactionResults( +bool CollectProposeTransactionResults( const NKikimr::NSchemeShard::TOperationId& operationId, const TEvColumnShard::TEvProposeTransactionResult::TPtr& ev, NKikimr::NSchemeShard::TOperationContext& context) @@ -190,7 +576,7 @@ bool NTableState::CollectProposeTransactionResults( return CollectProposeTxResults(ev, operationId, context, prepared, toString); } -bool NTableState::CollectSchemaChanged( +bool CollectSchemaChanged( const TOperationId& operationId, const TEvDataShard::TEvSchemaChanged::TPtr& ev, TOperationContext& context) @@ -263,7 +649,7 @@ bool NTableState::CollectSchemaChanged( return false; } -void NTableState::AckAllSchemaChanges(const TOperationId &operationId, TTxState &txState, TOperationContext &context) { +void AckAllSchemaChanges(const TOperationId &operationId, TTxState &txState, TOperationContext &context) { TTabletId ssId = context.SS->SelfTabletId(); LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, @@ -290,7 +676,7 @@ void NTableState::AckAllSchemaChanges(const TOperationId &operationId, TTxState } } -bool NTableState::CheckPartitioningChangedForTableModification(TTxState &txState, TOperationContext &context) { +bool CheckPartitioningChangedForTableModification(TTxState &txState, TOperationContext &context) { Y_ABORT_UNLESS(context.SS->Tables.contains(txState.TargetPathId)); TTableInfo::TPtr table = context.SS->Tables.at(txState.TargetPathId); @@ -309,7 +695,7 @@ bool NTableState::CheckPartitioningChangedForTableModification(TTxState &txState return !shardIdxsLeft.empty(); } -void NTableState::UpdatePartitioningForTableModification(TOperationId operationId, TTxState &txState, TOperationContext &context) { +void UpdatePartitioningForTableModification(TOperationId operationId, TTxState &txState, TOperationContext &context) { Y_ABORT_UNLESS(!txState.TxShardsListFinalized, "Rebuilding the list of shards must not happen twice"); NIceDb::TNiceDb db(context.GetDB()); @@ -428,7 +814,7 @@ void NTableState::UpdatePartitioningForTableModification(TOperationId operationI txState.TxShardsListFinalized = true; } -bool NTableState::SourceTablePartitioningChangedForCopyTable(const TTxState &txState, TOperationContext &context) { +bool SourceTablePartitioningChangedForCopyTable(const TTxState &txState, TOperationContext &context) { Y_ABORT_UNLESS(txState.SourcePathId != InvalidPathId); Y_ABORT_UNLESS(txState.TargetPathId != InvalidPathId); const TTableInfo::TPtr srcTableInfo = *context.SS->Tables.FindPtr(txState.SourcePathId); @@ -453,7 +839,7 @@ bool NTableState::SourceTablePartitioningChangedForCopyTable(const TTxState &txS return !srcShardIdxsLeft.empty(); } -void NTableState::UpdatePartitioningForCopyTable(TOperationId operationId, TTxState &txState, TOperationContext &context) { +void UpdatePartitioningForCopyTable(TOperationId operationId, TTxState &txState, TOperationContext &context) { Y_ABORT_UNLESS(!txState.TxShardsListFinalized, "CopyTable can adjust partitioning only once"); // Source table must not be altered or drop while we are performing copying. So we put it into a special state. @@ -555,7 +941,7 @@ void NTableState::UpdatePartitioningForCopyTable(TOperationId operationId, TTxSt txState.TxShardsListFinalized = true; } -TVector<TTableShardInfo> NTableState::ApplyPartitioningCopyTable(const TShardInfo &templateDatashardInfo, TTableInfo::TPtr srcTableInfo, TTxState &txState, TSchemeShard *ss) { +TVector<TTableShardInfo> ApplyPartitioningCopyTable(const TShardInfo &templateDatashardInfo, TTableInfo::TPtr srcTableInfo, TTxState &txState, TSchemeShard *ss) { TVector<TTableShardInfo> dstPartitions = srcTableInfo->GetPartitions(); // Source table must not be altered or drop while we are performing copying. So we put it into a special state. @@ -580,336 +966,109 @@ TVector<TTableShardInfo> NTableState::ApplyPartitioningCopyTable(const TShardInf return dstPartitions; } -namespace NPQState { - -bool CollectProposeTransactionResults(const TOperationId& operationId, - const TEvPersQueue::TEvProposeTransactionResult::TPtr& ev, - TOperationContext& context) -{ - auto prepared = [](NKikimrPQ::TEvProposeTransactionResult::EStatus status) -> bool { - return status == NKikimrPQ::TEvProposeTransactionResult::PREPARED; - }; - - auto toString = [](NKikimrPQ::TEvProposeTransactionResult::EStatus status) -> TString { - return NKikimrPQ::TEvProposeTransactionResult_EStatus_Name(status); - }; - - return CollectProposeTxResults(ev, operationId, context, prepared, toString); -} - -bool CollectPQConfigChanged(const TOperationId& operationId, - const TEvPersQueue::TEvProposeTransactionResult::TPtr& ev, - TOperationContext& context) -{ - Y_ABORT_UNLESS(context.SS->FindTx(operationId)); - TTxState& txState = *context.SS->FindTx(operationId); - - const auto& evRecord = ev->Get()->Record; - if (evRecord.GetStatus() == NKikimrPQ::TEvProposeTransactionResult::COMPLETE) { - const auto ssId = context.SS->SelfTabletId(); - const TTabletId shardId(evRecord.GetOrigin()); - - const auto shardIdx = context.SS->MustGetShardIdx(shardId); - Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shardIdx)); - - Y_ABORT_UNLESS(txState.State == TTxState::Propose); - - txState.ShardsInProgress.erase(shardIdx); - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "CollectPQConfigChanged accept TEvPersQueue::TEvProposeTransactionResult" - << ", operationId: " << operationId - << ", shardIdx: " << shardIdx - << ", shard: " << shardId - << ", left await: " << txState.ShardsInProgress.size() - << ", txState.State: " << TTxState::StateName(txState.State) - << ", txState.ReadyForNotifications: " << txState.ReadyForNotifications - << ", at schemeshard: " << ssId); - } - - return txState.ShardsInProgress.empty(); -} - -bool CollectPQConfigChanged(const TOperationId& operationId, - const TEvPersQueue::TEvProposeTransactionAttachResult::TPtr& ev, - TOperationContext& context) +// NTableState::TProposedWaitParts +// +TProposedWaitParts::TProposedWaitParts(TOperationId id, TTxState::ETxState nextState) + : OperationId(id) + , NextState(nextState) { - Y_ABORT_UNLESS(context.SS->FindTx(operationId)); - TTxState& txState = *context.SS->FindTx(operationId); - - // - // The PQ tablet can perform a transaction and send a TEvProposeTransactionResult(COMPLETE) response. - // The SchemeShard tablet can restart at this point. After restarting at the TPropose step, it will - // send the TEvProposeTransactionAttach message to the PQ tablets. If the NODATA status is specified in - // the response TEvProposeTransactionAttachResult, then the PQ tablet has already completed the transaction. - // Otherwise, she continues to execute the transaction - // - - const auto& evRecord = ev->Get()->Record; - if (evRecord.GetStatus() != NKikimrProto::NODATA) { - // - // If the PQ tablet returned something other than NODATA, then it continues to execute the transaction - // - return txState.ShardsInProgress.empty(); - } - - // - // Otherwise, she has already completed the transaction and has forgotten about it. Then we can - // remove PQ tablet from the list of shards - // - - const auto ssId = context.SS->SelfTabletId(); - const TTabletId shardId(evRecord.GetTabletId()); - - const auto shardIdx = context.SS->MustGetShardIdx(shardId); - Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shardIdx)); - - Y_ABORT_UNLESS(txState.State == TTxState::Propose); - - txState.ShardsInProgress.erase(shardIdx); - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "CollectPQConfigChanged accept TEvPersQueue::TEvProposeTransactionAttachResult" - << ", operationId: " << operationId - << ", shardIdx: " << shardIdx - << ", shard: " << shardId - << ", left await: " << txState.ShardsInProgress.size() - << ", txState.State: " << TTxState::StateName(txState.State) - << ", txState.ReadyForNotifications: " << txState.ReadyForNotifications - << ", at schemeshard: " << ssId); - - return txState.ShardsInProgress.empty(); -} - -bool TConfigureParts::HandleReply(TEvPersQueue::TEvProposeTransactionResult::TPtr& ev, TOperationContext& context) -{ - const TTabletId ssId = context.SS->SelfTabletId(); - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvProposeTransactionResult" - << ", at schemeshard: " << ssId); - - return CollectProposeTransactionResults(OperationId, ev, context); + IgnoreMessages(DebugHint(), + { TEvHive::TEvCreateTabletReply::EventType + , TEvDataShard::TEvProposeTransactionResult::EventType + , TEvPrivate::TEvOperationPlan::EventType } + ); } -THolder<TEvPersQueue::TEvProposeTransaction> TConfigureParts::MakeEvProposeTransaction(TTxId txId, - const TTopicInfo& pqGroup, - const TTopicTabletInfo& pqShard, - const TString& topicName, - const TString& topicPath, - const std::optional<TBootstrapConfigWrapper>& bootstrapConfig, - const TString& cloudId, - const TString& folderId, - const TString& databaseId, - const TString& databasePath, - TTxState::ETxType txType, - const TOperationContext& context) -{ - auto event = MakeHolder<TEvPersQueue::TEvProposeTransactionBuilder>(); - event->Record.SetTxId(ui64(txId)); - ActorIdToProto(context.SS->SelfId(), event->Record.MutableSourceActor()); - - MakePQTabletConfig(context, - *event->Record.MutableConfig()->MutableTabletConfig(), - pqGroup, - pqShard, - topicName, - topicPath, - cloudId, - folderId, - databaseId, - databasePath); - if (bootstrapConfig) { - Y_ABORT_UNLESS(txType == TTxState::TxCreatePQGroup); - event->PreSerializedData += bootstrapConfig->GetPreSerializedProposeTransaction(); - } - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "Propose configure PersQueue" << - ", message: " << event->Record.ShortUtf8DebugString()); - - return event; -} - -THolder<TEvPersQueue::TEvUpdateConfig> TConfigureParts::MakeEvUpdateConfig(TTxId txId, - const TTopicInfo& pqGroup, - const TTopicTabletInfo& pqShard, - const TString& topicName, - const TString& topicPath, - const std::optional<TBootstrapConfigWrapper>& bootstrapConfig, - const TString& cloudId, - const TString& folderId, - const TString& databaseId, - const TString& databasePath, - TTxState::ETxType txType, - const TOperationContext& context) -{ - auto event = MakeHolder<TEvPersQueue::TEvUpdateConfigBuilder>(); - event->Record.SetTxId(ui64(txId)); - - MakePQTabletConfig(context, - *event->Record.MutableTabletConfig(), - pqGroup, - pqShard, - topicName, - topicPath, - cloudId, - folderId, - databaseId, - databasePath); - if (bootstrapConfig) { - Y_ABORT_UNLESS(txType == TTxState::TxCreatePQGroup); - event->PreSerializedData += bootstrapConfig->GetPreSerializedUpdateConfig(); - } - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "Propose configure PersQueue" << - ", message: " << event->Record.ShortUtf8DebugString()); - - return event; -} - -bool TPropose::HandleReply(TEvPersQueue::TEvProposeTransactionResult::TPtr& ev, TOperationContext& context) -{ - const TTabletId ssId = context.SS->SelfTabletId(); - const auto& evRecord = ev->Get()->Record; - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvProposeTransactionResult" - << " triggers early" - << ", at schemeshard: " << ssId - << " message# " << evRecord.ShortDebugString()); - - const bool collected = CollectPQConfigChanged(OperationId, ev, context); - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvProposeTransactionResult" - << " CollectPQConfigChanged: " << (collected ? "true" : "false")); - - return TryPersistState(context); -} - -bool TPropose::HandleReply(TEvPersQueue::TEvProposeTransactionAttachResult::TPtr& ev, TOperationContext& context) -{ - const auto ssId = context.SS->SelfTabletId(); +bool TProposedWaitParts::HandleReply(TEvDataShard::TEvSchemaChanged::TPtr& ev, TOperationContext& context) { + TTabletId ssId = context.SS->SelfTabletId(); const auto& evRecord = ev->Get()->Record; LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvProposeTransactionAttachResult" - << " triggers early" - << ", at schemeshard: " << ssId - << " message# " << evRecord.ShortDebugString()); - - const bool collected = CollectPQConfigChanged(OperationId, ev, context); + DebugHint() << " HandleReply TEvSchemaChanged" + << " at tablet: " << ssId); LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvProposeTransactionAttachResult" - << " CollectPQConfigChanged: " << (collected ? "true" : "false")); - - return TryPersistState(context); -} - -void TPropose::PrepareShards(TTxState& txState, TSet<TTabletId>& shardSet, TOperationContext& context) -{ - txState.UpdateShardsInProgress(); - - for (const auto& shard : txState.Shards) { - const TShardIdx idx = shard.Idx; - // - // The operation involves the ETabletType::PersQueue and Tabletype::PersQueueReadBalancer shards. - // The program receives responses from PersQueueReadBalancer at the previous stage. At this stage, - // it only expects TEvProposeTransactionResult from PersQueue - // - if (shard.TabletType == ETabletType::PersQueue) { - const TTabletId tablet = context.SS->ShardInfos.at(idx).TabletID; - - shardSet.insert(tablet); + DebugHint() << " HandleReply TEvSchemaChanged" + << " at tablet: " << ssId + << " message: " << evRecord.ShortDebugString()); - // - // By this point, the SchemeShard tablet could restart and the actor ID changed. Therefore, we send - // the TEvProposeTransactionAttach message to the PQ tablets so that they recognize the new recipient - // - SendEvProposeTransactionAttach(idx, tablet, context); - } else { - txState.ShardsInProgress.erase(idx); - } - } -} - -void TPropose::SendEvProposeTransactionAttach(TShardIdx shard, TTabletId tablet, - TOperationContext& context) -{ - auto event = - MakeHolder<TEvPersQueue::TEvProposeTransactionAttach>(ui64(tablet), - ui64(OperationId.GetTxId())); - context.OnComplete.BindMsgToPipe(OperationId, tablet, shard, event.Release()); -} - -bool TPropose::CanPersistState(const TTxState& txState, - TOperationContext& context) -{ - if (!txState.ShardsInProgress.empty()) { + if (!CollectSchemaChanged(OperationId, ev, context)) { LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " can't persist state: " << - "ShardsInProgress is not empty, remain: " << txState.ShardsInProgress.size()); + DebugHint() << " HandleReply TEvSchemaChanged" + << " CollectSchemaChanged: false"); return false; } - PathId = txState.TargetPathId; - Path = context.SS->PathsById.at(PathId); + Y_ABORT_UNLESS(context.SS->FindTx(OperationId)); + TTxState& txState = *context.SS->FindTx(OperationId); - if (Path->StepCreated == InvalidStepId) { + if (!txState.ReadyForNotifications) { LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " can't persist state: " << - "StepCreated is invalid"); + DebugHint() << " HandleReply TEvSchemaChanged" + << " ReadyForNotifications: false"); return false; } return true; } -void TPropose::PersistState(const TTxState& txState, - TOperationContext& context) const -{ - NIceDb::TNiceDb db(context.GetDB()); +bool TProposedWaitParts::ProgressState(TOperationContext& context) { + TTabletId ssId = context.SS->SelfTabletId(); - if (txState.TxType == TTxState::TxCreatePQGroup) { - auto parentDir = context.SS->PathsById.at(Path->ParentPathId); - ++parentDir->DirAlterVersion; - context.SS->PersistPathDirAlterVersion(db, parentDir); - context.SS->ClearDescribePathCaches(parentDir); - context.OnComplete.PublishToSchemeBoard(OperationId, parentDir->PathId); - } + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " ProgressState" + << " at tablet: " << ssId); - context.SS->ClearDescribePathCaches(Path); - context.OnComplete.PublishToSchemeBoard(OperationId, PathId); + TTxState* txState = context.SS->FindTx(OperationId); - TTopicInfo::TPtr pqGroup = context.SS->Topics[PathId]; + NIceDb::TNiceDb db(context.GetDB()); - NKikimrPQ::TPQTabletConfig tabletConfig = pqGroup->GetTabletConfig(); - NKikimrPQ::TPQTabletConfig newTabletConfig = pqGroup->AlterData->GetTabletConfig(); + txState->ClearShardsInProgress(); + for (TTxState::TShardOperation& shard : txState->Shards) { + if (shard.Operation < TTxState::ProposedWaitParts) { + shard.Operation = TTxState::ProposedWaitParts; + context.SS->PersistUpdateTxShard(db, OperationId, shard.Idx, shard.Operation); + } + Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shard.Idx)); + context.OnComplete.RouteByTablet(OperationId, context.SS->ShardInfos.at(shard.Idx).TabletID); + } + txState->UpdateShardsInProgress(TTxState::ProposedWaitParts); - pqGroup->FinishAlter(); + // Move all notifications that were already received + // NOTE: SchemeChangeNotification is sent form DS after it has got PlanStep from coordinator and the schema tx has completed + // At that moment the SS might not have received PlanStep from coordinator yet (this message might be still on its way to SS) + // So we are going to accumulate SchemeChangeNotification that are received before this Tx switches to WaitParts state + txState->AcceptPendingSchemeNotification(); - context.SS->PersistPersQueueGroup(db, PathId, pqGroup); - context.SS->PersistRemovePersQueueGroupAlter(db, PathId); + // Got notifications from all datashards? + if (txState->ShardsInProgress.empty()) { + AckAllSchemaChanges(OperationId, *txState, context); + context.SS->ChangeTxState(db, OperationId, NextState); + return true; + } - context.SS->ChangeTxState(db, OperationId, TTxState::Done); + return false; } -bool TPropose::TryPersistState(TOperationContext& context) -{ - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); +} // namespace NTableState - if (!CanPersistState(*txState, context)) { - return false; - } +namespace NPQState { + +bool CollectProposeTransactionResults(const TOperationId& operationId, + const TEvPersQueue::TEvProposeTransactionResult::TPtr& ev, + TOperationContext& context) +{ + auto prepared = [](NKikimrPQ::TEvProposeTransactionResult::EStatus status) -> bool { + return status == NKikimrPQ::TEvProposeTransactionResult::PREPARED; + }; - PersistState(*txState, context); + auto toString = [](NKikimrPQ::TEvProposeTransactionResult::EStatus status) -> TString { + return NKikimrPQ::TEvProposeTransactionResult_EStatus_Name(status); + }; - return true; + return CollectProposeTxResults(ev, operationId, context, prepared, toString); } -} +} // namespace NPQState TSet<ui32> AllIncomingEvents() { TSet<ui32> result; @@ -923,7 +1082,9 @@ TSet<ui32> AllIncomingEvents() { return result; } -void NForceDrop::CollectShards(const THashSet<TPathId>& paths, TOperationId operationId, TTxState *txState, TOperationContext &context) { +namespace NForceDrop { + +void CollectShards(const THashSet<TPathId>& paths, TOperationId operationId, TTxState *txState, TOperationContext &context) { NIceDb::TNiceDb db(context.GetDB()); auto shards = context.SS->CollectAllShards(paths); @@ -950,7 +1111,7 @@ void NForceDrop::CollectShards(const THashSet<TPathId>& paths, TOperationId oper context.SS->PersistTxState(db, operationId); } -void NForceDrop::ValidateNoTransactionOnPaths(TOperationId operationId, const THashSet<TPathId>& paths, TOperationContext &context) { +void ValidateNoTransactionOnPaths(TOperationId operationId, const THashSet<TPathId>& paths, TOperationContext &context) { // No transaction should materialize in a subdomain that is being deleted -- // -- all operations should be checking parent dir status at Propose stage. // However, it is better to verify that, just in case. @@ -963,6 +1124,8 @@ void NForceDrop::ValidateNoTransactionOnPaths(TOperationId operationId, const TH } } +} // namespace NForceDrop + void IncParentDirAlterVersionWithRepublishSafeWithUndo(const TOperationId& opId, const TPath& path, TSchemeShard* ss, TSideEffects& onComplete) { auto parent = path.Parent(); if (parent.Base()->IsDirectory() || parent.Base()->IsDomainRoot()) { diff --git a/ydb/core/tx/schemeshard/schemeshard__operation_common.h b/ydb/core/tx/schemeshard/schemeshard__operation_common.h index 701a0f00726..7c8372a8782 100644 --- a/ydb/core/tx/schemeshard/schemeshard__operation_common.h +++ b/ydb/core/tx/schemeshard/schemeshard__operation_common.h @@ -8,8 +8,9 @@ #include <ydb/core/tx/columnshard/columnshard.h> #include <ydb/core/tx/tx_processing.h> -namespace NKikimr { -namespace NSchemeShard { +namespace NKikimr::NSchemeShard { + +class TSchemeShard; TSet<ui32> AllIncomingEvents(); @@ -52,86 +53,10 @@ private: } public: - TProposedWaitParts(TOperationId id, TTxState::ETxState nextState = TTxState::Done) - : OperationId(id) - , NextState(nextState) - { - IgnoreMessages(DebugHint(), - { TEvHive::TEvCreateTabletReply::EventType - , TEvDataShard::TEvProposeTransactionResult::EventType - , TEvPrivate::TEvOperationPlan::EventType } - ); - } - - bool HandleReply(TEvDataShard::TEvSchemaChanged::TPtr& ev, TOperationContext& context) override { - TTabletId ssId = context.SS->SelfTabletId(); - const auto& evRecord = ev->Get()->Record; - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvSchemaChanged" - << " at tablet: " << ssId); - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvSchemaChanged" - << " at tablet: " << ssId - << " message: " << evRecord.ShortDebugString()); - - if (!NTableState::CollectSchemaChanged(OperationId, ev, context)) { - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvSchemaChanged" - << " CollectSchemaChanged: false"); - return false; - } - - Y_ABORT_UNLESS(context.SS->FindTx(OperationId)); - TTxState& txState = *context.SS->FindTx(OperationId); - - if (!txState.ReadyForNotifications) { - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvSchemaChanged" - << " ReadyForNotifications: false"); - return false; - } - - return true; - } - - bool ProgressState(TOperationContext& context) override { - TTabletId ssId = context.SS->SelfTabletId(); + TProposedWaitParts(TOperationId id, TTxState::ETxState nextState = TTxState::Done); - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " ProgressState" - << " at tablet: " << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - - NIceDb::TNiceDb db(context.GetDB()); - - txState->ClearShardsInProgress(); - for (TTxState::TShardOperation& shard : txState->Shards) { - if (shard.Operation < TTxState::ProposedWaitParts) { - shard.Operation = TTxState::ProposedWaitParts; - context.SS->PersistUpdateTxShard(db, OperationId, shard.Idx, shard.Operation); - } - Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shard.Idx)); - context.OnComplete.RouteByTablet(OperationId, context.SS->ShardInfos.at(shard.Idx).TabletID); - } - txState->UpdateShardsInProgress(TTxState::ProposedWaitParts); - - // Move all notifications that were already received - // NOTE: SchemeChangeNotification is sent form DS after it has got PlanStep from coordinator and the schema tx has completed - // At that moment the SS might not have received PlanStep from coordinator yet (this message might be still on its way to SS) - // So we are going to accumulate SchemeChangeNotification that are received before this Tx switches to WaitParts state - txState->AcceptPendingSchemeNotification(); - - // Got notifications from all datashards? - if (txState->ShardsInProgress.empty()) { - NTableState::AckAllSchemaChanges(OperationId, *txState, context); - context.SS->ChangeTxState(db, OperationId, NextState); - return true; - } - - return false; - } + bool ProgressState(TOperationContext& context) override; + bool HandleReply(TEvDataShard::TEvSchemaChanged::TPtr& ev, TOperationContext& context) override; }; } // namespace NTableState @@ -144,285 +69,14 @@ class TCreateParts: public TSubOperationState { << " opId# " << OperationId; } -public: - explicit TCreateParts(const TOperationId& id) - : OperationId(id) - { - IgnoreMessages(DebugHint(), {}); - } - - bool HandleReply(TEvHive::TEvAdoptTabletReply::TPtr& ev, TOperationContext& context) override { - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvAdoptTablet" - << ", at tabletId: " << context.SS->TabletID()); - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvAdoptTablet" - << ", message% " << DebugReply(ev)); - - NIceDb::TNiceDb db(context.GetDB()); - - const TString& explain = ev->Get()->Record.GetExplain(); - TTabletId tabletId = TTabletId(ev->Get()->Record.GetTabletID()); // global id from hive - auto shardIdx = context.SS->MakeLocalId(TLocalShardIdx(ev->Get()->Record.GetOwnerIdx())); // global id from hive - TTabletId hive = TTabletId(ev->Get()->Record.GetOrigin()); - - Y_ABORT_UNLESS(context.SS->TabletID() == ev->Get()->Record.GetOwner()); - - NKikimrProto::EReplyStatus status = ev->Get()->Record.GetStatus(); - Y_VERIFY_S(status == NKikimrProto::OK || status == NKikimrProto::ALREADY, - "Unexpected status " << NKikimrProto::EReplyStatus_Name(status) - << " in AdoptTabletReply for tabletId " << tabletId - << " with explain " << explain); - - // Note that HIVE might send duplicate TTxAdoptTabletReply in case of restarts - // So we just ignore the event if we cannot find the Tx or if it is in a different - // state - - Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shardIdx)); - - if (!context.SS->AdoptedShards.contains(shardIdx)) { - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvAdoptTablet" - << " Got TTxAdoptTabletReply for shard but it is not present in AdoptedShards" - << ", shardIdx: " << shardIdx - << ", tabletId:" << tabletId); - return false; - } - - TShardInfo& shardInfo = context.SS->ShardInfos[shardIdx]; - Y_ABORT_UNLESS(shardInfo.TabletID == InvalidTabletId || shardInfo.TabletID == tabletId); - - Y_ABORT_UNLESS(tabletId != InvalidTabletId); - shardInfo.TabletID = tabletId; - context.SS->TabletIdToShardIdx[tabletId] = shardIdx; - - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(txState->State == TTxState::CreateParts); - - txState->ShardsInProgress.erase(shardIdx); - - context.SS->AdoptedShards.erase(shardIdx); - context.SS->PersistDeleteAdopted(db, shardIdx); - context.SS->PersistShardMapping(db, shardIdx, tabletId, shardInfo.PathId, OperationId.GetTxId(), shardInfo.TabletType); - - context.OnComplete.UnbindMsgFromPipe(OperationId, hive, shardIdx); - context.OnComplete.ActivateShardCreated(shardIdx, OperationId.GetTxId()); - - // If all datashards have been created - if (txState->ShardsInProgress.empty()) { - context.SS->ChangeTxState(db, OperationId, TTxState::ConfigureParts); - return true; - } - - return false; - } - - bool HandleReply(TEvHive::TEvCreateTabletReply::TPtr& ev, TOperationContext& context) override { - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvCreateTabletReply" - << ", at tabletId: " << context.SS->TabletID()); - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvCreateTabletReply" - << ", message: " << DebugReply(ev)); - - NIceDb::TNiceDb db(context.GetDB()); - - auto shardIdx = TShardIdx(ev->Get()->Record.GetOwner(), - TLocalShardIdx(ev->Get()->Record.GetOwnerIdx())); - - auto tabletId = TTabletId(ev->Get()->Record.GetTabletID()); // global id from hive - auto hive = TTabletId(ev->Get()->Record.GetOrigin()); - - NKikimrProto::EReplyStatus status = ev->Get()->Record.GetStatus(); - - Y_VERIFY_S(status == NKikimrProto::OK - || status == NKikimrProto::ALREADY - || status == NKikimrProto::INVALID_OWNER - || status == NKikimrProto::BLOCKED, - "Unexpected status " << NKikimrProto::EReplyStatus_Name(status) - << " in CreateTabletReply shard idx " << shardIdx << " tabletId " << tabletId); - - if (status == NKikimrProto::BLOCKED) { - Y_ABORT_UNLESS(!context.SS->IsDomainSchemeShard); - - LOG_NOTICE_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " CreateRequest BLOCKED " - << " at Hive: " << hive - << " msg: " << DebugReply(ev)); - - // do not unsubscribe message - // context.OnComplete.UnbindMsgFromPipe(OperationId, hive, shardIdx); - - // just stay calm and hung until tenant schemeshard is deleted - return false; - } - - TTxState& txState = *context.SS->FindTx(OperationId); - - TShardInfo& shardInfo = context.SS->ShardInfos.at(shardIdx); - Y_ABORT_UNLESS(shardInfo.TabletID == InvalidTabletId || shardInfo.TabletID == tabletId); - - if (status == NKikimrProto::INVALID_OWNER) { - auto redirectTo = TTabletId(ev->Get()->Record.GetForwardRequest().GetHiveTabletId()); - Y_ABORT_UNLESS(redirectTo); - Y_ABORT_UNLESS(tabletId); - - context.OnComplete.UnbindMsgFromPipe(OperationId, hive, shardIdx); - - auto path = context.SS->PathsById.at(txState.TargetPathId); - auto request = CreateEvCreateTablet(path, shardIdx, context); - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " CreateRequest" - << " Redirect from Hive: " << hive - << " to Hive: " << redirectTo - << " msg: " << request->Record.DebugString()); - - context.OnComplete.BindMsgToPipe(OperationId, redirectTo, shardIdx, request.Release()); - return false; - } - - if (shardInfo.TabletID == InvalidTabletId) { - switch (shardInfo.TabletType) { - case ETabletType::DataShard: - context.SS->TabletCounters->Simple()[COUNTER_TABLE_SHARD_ACTIVE_COUNT].Add(1); - break; - case ETabletType::Coordinator: - context.SS->TabletCounters->Simple()[COUNTER_SUB_DOMAIN_COORDINATOR_COUNT].Add(1); - break; - case ETabletType::Mediator: - context.SS->TabletCounters->Simple()[COUNTER_SUB_DOMAIN_MEDIATOR_COUNT].Add(1); - break; - case ETabletType::Hive: - context.SS->TabletCounters->Simple()[COUNTER_SUB_DOMAIN_HIVE_COUNT].Add(1); - break; - case ETabletType::SysViewProcessor: - context.SS->TabletCounters->Simple()[COUNTER_SYS_VIEW_PROCESSOR_COUNT].Add(1); - break; - case ETabletType::StatisticsAggregator: - context.SS->TabletCounters->Simple()[COUNTER_STATISTICS_AGGREGATOR_COUNT].Add(1); - break; - case ETabletType::BackupController: - context.SS->TabletCounters->Simple()[COUNTER_BACKUP_CONTROLLER_TABLET_COUNT].Add(1); - break; - default: - break; - } - } - - Y_ABORT_UNLESS(tabletId != InvalidTabletId); - shardInfo.TabletID = tabletId; - context.SS->TabletIdToShardIdx[tabletId] = shardIdx; - - Y_ABORT_UNLESS(OperationId.GetTxId() == shardInfo.CurrentTxId); - - txState.ShardsInProgress.erase(shardIdx); - - context.SS->PersistShardMapping(db, shardIdx, tabletId, shardInfo.PathId, OperationId.GetTxId(), shardInfo.TabletType); - context.OnComplete.UnbindMsgFromPipe(OperationId, hive, shardIdx); - context.OnComplete.ActivateShardCreated(shardIdx, OperationId.GetTxId()); - - // If all datashards have been created - if (txState.ShardsInProgress.empty()) { - context.SS->ChangeTxState(db, OperationId, TTxState::ConfigureParts); - return true; - } - - return false; - } - - THolder<TEvHive::TEvAdoptTablet> AdoptRequest(TShardIdx shardIdx, TOperationContext& context) { - Y_ABORT_UNLESS(context.SS->AdoptedShards.contains(shardIdx)); - auto& adoptedShard = context.SS->AdoptedShards[shardIdx]; - auto& shard = context.SS->ShardInfos[shardIdx]; - - THolder<TEvHive::TEvAdoptTablet> ev = MakeHolder<TEvHive::TEvAdoptTablet>( - ui64(shard.TabletID), - adoptedShard.PrevOwner, ui64(adoptedShard.PrevShardIdx), - shard.TabletType, - context.SS->TabletID(), ui64(shardIdx.GetLocalId())); - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " AdoptRequest" - << " Event to Hive: " << ev->Record.DebugString().c_str()); - - return ev; - } - - bool ProgressState(TOperationContext& context) override { - TTabletId ssId = context.SS->SelfTabletId(); - - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " ProgressState" - << ", operation type: " << TTxState::TypeName(txState->TxType) - << ", at tablet" << ssId); - - if (txState->TxType == TTxState::TxDropTable - || txState->TxType == TTxState::TxAlterTable - || txState->TxType == TTxState::TxBackup - || txState->TxType == TTxState::TxRestore) { - if (NTableState::CheckPartitioningChangedForTableModification(*txState, context)) { - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " ProgressState" - << " SourceTablePartitioningChangedForModification" - << ", tx type: " << TTxState::TypeName(txState->TxType)); - NTableState::UpdatePartitioningForTableModification(OperationId, *txState, context); - } - } else if (txState->TxType == TTxState::TxCopyTable) { - if (NTableState::SourceTablePartitioningChangedForCopyTable(*txState, context)) { - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " ProgressState" - << " SourceTablePartitioningChangedForCopyTable" - << ", tx type: " << TTxState::TypeName(txState->TxType)); - NTableState::UpdatePartitioningForCopyTable(OperationId, *txState, context); - } - } - - txState->ClearShardsInProgress(); - - bool nothingToDo = true; - for (const auto& shard : txState->Shards) { - if (shard.Operation != TTxState::CreateParts) { - continue; - } - nothingToDo = false; - - if (context.SS->AdoptedShards.contains(shard.Idx)) { - auto ev = AdoptRequest(shard.Idx, context); - context.OnComplete.BindMsgToPipe(OperationId, context.SS->GetGlobalHive(context.Ctx), shard.Idx, ev.Release()); - } else { - auto path = context.SS->PathsById.at(txState->TargetPathId); - auto ev = CreateEvCreateTablet(path, shard.Idx, context); + THolder<TEvHive::TEvAdoptTablet> AdoptRequest(TShardIdx shardIdx, TOperationContext& context); - auto hiveToRequest = context.SS->ResolveHive(shard.Idx, context.Ctx); - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " CreateRequest" - << " Event to Hive: " << hiveToRequest - << " msg: "<< ev->Record.DebugString().c_str()); - - context.OnComplete.BindMsgToPipe(OperationId, hiveToRequest, shard.Idx, ev.Release()); - } - context.OnComplete.RouteByShardIdx(OperationId, shard.Idx); - } - - if (nothingToDo) { - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " ProgressState" - << " no shards to create, do next state"); - - NIceDb::TNiceDb db(context.GetDB()); - context.SS->ChangeTxState(db, OperationId, TTxState::ConfigureParts); - return true; - } +public: + explicit TCreateParts(const TOperationId& id); - txState->UpdateShardsInProgress(TTxState::CreateParts); - return false; - } + bool ProgressState(TOperationContext& context) override; + bool HandleReply(TEvHive::TEvCreateTabletReply::TPtr& ev, TOperationContext& context) override; + bool HandleReply(TEvHive::TEvAdoptTabletReply::TPtr& ev, TOperationContext& context) override; }; class TDeleteParts: public TSubOperationState { @@ -435,50 +89,19 @@ protected: << " opId# " << OperationId << " "; } - void DeleteShards(TOperationContext& context) { - const auto* txState = context.SS->FindTx(OperationId); - - // Initiate asynchronous deletion of all shards - for (const auto& shard : txState->Shards) { - context.OnComplete.DeleteShard(shard.Idx); - } - } + void DeleteShards(TOperationContext& context); public: - explicit TDeleteParts(const TOperationId& id, TTxState::ETxState nextState = TTxState::Propose) - : OperationId(id) - , NextState(nextState) - { - IgnoreMessages(DebugHint(), {}); - } + explicit TDeleteParts(const TOperationId& id, TTxState::ETxState nextState = TTxState::Propose); - bool ProgressState(TOperationContext& context) override { - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "[" << context.SS->TabletID() << "] " << DebugHint() << " ProgressState"); - DeleteShards(context); - - NIceDb::TNiceDb db(context.GetDB()); - context.SS->ChangeTxState(db, OperationId, NextState); - return true; - } + bool ProgressState(TOperationContext& context) override; }; class TDeletePartsAndDone: public TDeleteParts { public: - explicit TDeletePartsAndDone(const TOperationId& id) - : TDeleteParts(id) - { - Y_UNUSED(NextState); - } + explicit TDeletePartsAndDone(const TOperationId& id); - bool ProgressState(TOperationContext& context) override { - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "[" << context.SS->TabletID() << "] " << DebugHint() << " ProgressState"); - DeleteShards(context); - - context.OnComplete.DoneOperation(OperationId); - return true; - } + bool ProgressState(TOperationContext& context) override; }; class TDone: public TSubOperationState { @@ -491,114 +114,14 @@ protected: } public: - explicit TDone(const TOperationId& id) - : OperationId(id) - { - IgnoreMessages(DebugHint(), AllIncomingEvents()); - } - - bool ProgressState(TOperationContext& context) override { - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "[" << context.SS->TabletID() << "] " << DebugHint() << " ProgressState"); - - const auto* txState = context.SS->FindTx(OperationId); - - const auto& pathId = txState->TargetPathId; - Y_ABORT_UNLESS(context.SS->PathsById.contains(pathId)); - TPathElement::TPtr path = context.SS->PathsById.at(pathId); - Y_VERIFY_S(path->PathState != TPathElement::EPathState::EPathStateNoChanges, "with context" - << ", PathState: " << NKikimrSchemeOp::EPathState_Name(path->PathState) - << ", PathId: " << path->PathId - << ", PathName: " << path->Name); + explicit TDone(const TOperationId& id); - if (path->IsPQGroup() && txState->IsCreate()) { - TPathElement::TPtr parentDir = context.SS->PathsById.at(path->ParentPathId); - // can't be removed until KIKIMR-8861 - // at least lets wrap it into condition - // it helps to actualize PATHSTATE inside children listing - context.SS->ClearDescribePathCaches(parentDir); - } - - if (txState->IsDrop()) { - context.OnComplete.ReleasePathState(OperationId, path->PathId, TPathElement::EPathState::EPathStateNotExist); - } else { - context.OnComplete.ReleasePathState(OperationId, path->PathId, TPathElement::EPathState::EPathStateNoChanges); - } - - if (txState->SourcePathId != InvalidPathId) { - Y_ABORT_UNLESS(context.SS->PathsById.contains(txState->SourcePathId)); - TPathElement::TPtr srcPath = context.SS->PathsById.at(txState->SourcePathId); - if (srcPath->PathState == TPathElement::EPathState::EPathStateCopying) { - context.OnComplete.ReleasePathState(OperationId, srcPath->PathId, TPathElement::EPathState::EPathStateNoChanges); - } - } - - // OlapStore tracks all tables that are under operation, make sure to unlink - if (context.SS->ColumnTables.contains(pathId)) { - auto tableInfo = context.SS->ColumnTables.at(pathId); - if (!tableInfo->IsStandalone()) { - const auto storePathId = tableInfo->GetOlapStorePathIdVerified(); - if (context.SS->OlapStores.contains(storePathId)) { - auto storeInfo = context.SS->OlapStores.at(storePathId); - storeInfo->ColumnTablesUnderOperation.erase(pathId); - } - } - } - - context.OnComplete.DoneOperation(OperationId); - return true; - } + bool ProgressState(TOperationContext& context) override; }; namespace NPQState { -class TBootstrapConfigWrapper: public NKikimrPQ::TBootstrapConfig { - struct TSerializedProposeTransaction { - TString Value; - - static TSerializedProposeTransaction Serialize(const NKikimrPQ::TBootstrapConfig& value) { - NKikimrPQ::TEvProposeTransaction record; - record.MutableConfig()->MutableBootstrapConfig()->CopyFrom(value); - return {record.SerializeAsString()}; - } - }; - - struct TSerializedUpdateConfig { - TString Value; - - static TSerializedUpdateConfig Serialize(const NKikimrPQ::TBootstrapConfig& value) { - NKikimrPQ::TUpdateConfig record; - record.MutableBootstrapConfig()->CopyFrom(value); - return {record.SerializeAsString()}; - } - }; - - mutable std::optional<std::variant< - TSerializedProposeTransaction, - TSerializedUpdateConfig - >> PreSerialized; - - template <typename T> - const TString& Get() const { - if (!PreSerialized) { - PreSerialized.emplace(T::Serialize(*this)); - } - - const auto* value = std::get_if<T>(&PreSerialized.value()); - Y_ABORT_UNLESS(value); - - return value->Value; - } - -public: - const TString& GetPreSerializedProposeTransaction() const { - return Get<TSerializedProposeTransaction>(); - } - - const TString& GetPreSerializedUpdateConfig() const { - return Get<TSerializedUpdateConfig>(); - } -}; +bool CollectProposeTransactionResults(const TOperationId& operationId, const TEvPersQueue::TEvProposeTransactionResult::TPtr& ev, TOperationContext& context); class TConfigureParts: public TSubOperationState { private: @@ -611,397 +134,11 @@ private: } public: - TConfigureParts(TOperationId id) - : OperationId(id) - { - IgnoreMessages(DebugHint(), {TEvHive::TEvCreateTabletReply::EventType}); - } + TConfigureParts(TOperationId id); + bool ProgressState(TOperationContext& context) override; bool HandleReply(TEvPersQueue::TEvProposeTransactionResult::TPtr& ev, TOperationContext& context) override; - - bool HandleReply(TEvPersQueue::TEvUpdateConfigResponse::TPtr& ev, TOperationContext& context) override { - TTabletId ssId = context.SS->SelfTabletId(); - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvUpdateConfigResponse" - << " at tablet" << ssId); - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvUpdateConfigResponse" - << " message: " << ev->Get()->Record.ShortUtf8DebugString() - << " at tablet" << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreatePQGroup || txState->TxType == TTxState::TxAlterPQGroup); - - TTabletId tabletId = TTabletId(ev->Get()->Record.GetOrigin()); - NKikimrPQ::EStatus status = ev->Get()->Record.GetStatus(); - - // Schemeshard always sends a valid config to the PQ tablet and PQ tablet is not supposed to reject it - // Also Schemeshard never send multiple different config updates simultaneously (same config - // can be sent more than once in case of retries due to restarts or disconnects) - // If PQ tablet is not able to save a valid config it should kill itself and restart without - // sending error response - Y_VERIFY_S(status == NKikimrPQ::OK || status == NKikimrPQ::ERROR_UPDATE_IN_PROGRESS, - "Unexpected error in UpdateConfigResponse," - << " status: " << NKikimrPQ::EStatus_Name(status) - << " Tx " << OperationId - << " tablet "<< tabletId - << " at schemeshard: " << ssId); - - if (status == NKikimrPQ::ERROR_UPDATE_IN_PROGRESS) { - LOG_ERROR_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "PQ reconfiguration is in progress. We'll try to finish it later." - << " Tx " << OperationId - << " tablet " << tabletId - << " at schemeshard: " << ssId); - return false; - } - - Y_ABORT_UNLESS(txState->State == TTxState::ConfigureParts); - - TShardIdx idx = context.SS->MustGetShardIdx(tabletId); - txState->ShardsInProgress.erase(idx); - - // Detach datashard pipe - context.OnComplete.UnbindMsgFromPipe(OperationId, tabletId, idx); - - if (txState->ShardsInProgress.empty()) { - NIceDb::TNiceDb db(context.GetDB()); - context.SS->ChangeTxState(db, OperationId, TTxState::Propose); - return true; - } - - return false; - } - - bool ProgressState(TOperationContext& context) override { - TTabletId ssId = context.SS->SelfTabletId(); - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() - << " HandleReply ProgressState" - << ", at schemeshard: " << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreatePQGroup || txState->TxType == TTxState::TxAlterPQGroup); - - txState->ClearShardsInProgress(); - - TString topicName = context.SS->PathsById.at(txState->TargetPathId)->Name; - Y_VERIFY_S(topicName.size(), - "topicName is empty" - <<", pathId: " << txState->TargetPathId); - - TTopicInfo::TPtr pqGroup = context.SS->Topics[txState->TargetPathId]; - Y_VERIFY_S(pqGroup, - "pqGroup is null" - << ", pathId " << txState->TargetPathId); - - const TPathElement::TPtr dbRootEl = context.SS->PathsById.at(context.SS->RootPathId()); - TString cloudId; - if (dbRootEl->UserAttrs->Attrs.contains("cloud_id")) { - cloudId = dbRootEl->UserAttrs->Attrs.at("cloud_id"); - } - TString folderId; - if (dbRootEl->UserAttrs->Attrs.contains("folder_id")) { - folderId = dbRootEl->UserAttrs->Attrs.at("folder_id"); - } - TString databaseId; - if (dbRootEl->UserAttrs->Attrs.contains("database_id")) { - databaseId = dbRootEl->UserAttrs->Attrs.at("database_id"); - } - - TString databasePath = TPath::Init(context.SS->RootPathId(), context.SS).PathString(); - auto topicPath = TPath::Init(txState->TargetPathId, context.SS); - - std::optional<TBootstrapConfigWrapper> bootstrapConfig; - if (txState->TxType == TTxState::TxCreatePQGroup && topicPath.Parent().IsCdcStream()) { - bootstrapConfig.emplace(); - - auto tablePath = topicPath.Parent().Parent(); // table/cdc_stream/topic - Y_ABORT_UNLESS(tablePath.IsResolved()); - - Y_ABORT_UNLESS(context.SS->Tables.contains(tablePath.Base()->PathId)); - auto table = context.SS->Tables.at(tablePath.Base()->PathId); - - const auto& partitions = table->GetPartitions(); - - for (ui32 i = 0; i < partitions.size(); ++i) { - const auto& cur = partitions.at(i); - - Y_ABORT_UNLESS(context.SS->ShardInfos.contains(cur.ShardIdx)); - const auto& shard = context.SS->ShardInfos.at(cur.ShardIdx); - - auto& mg = *bootstrapConfig->AddExplicitMessageGroups(); - mg.SetId(NPQ::NSourceIdEncoding::EncodeSimple(ToString(shard.TabletID))); - - if (i != partitions.size() - 1) { - mg.MutableKeyRange()->SetToBound(cur.EndOfRange); - } - - if (i) { - const auto& prev = partitions.at(i - 1); - mg.MutableKeyRange()->SetFromBound(prev.EndOfRange); - } - } - } - - for (auto shard : txState->Shards) { - TShardIdx idx = shard.Idx; - TTabletId tabletId = context.SS->ShardInfos.at(idx).TabletID; - - if (shard.TabletType == ETabletType::PersQueue) { - TTopicTabletInfo::TPtr pqShard = pqGroup->Shards.at(idx); - Y_VERIFY_S(pqShard, "pqShard is null, idx is " << idx << " has was "<< THash<TShardIdx>()(idx)); - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "Propose configure PersQueue" - << ", opId: " << OperationId - << ", tabletId: " << tabletId - << ", Partitions size: " << pqShard->Partitions.size() - << ", at schemeshard: " << ssId); - - THolder<NActors::IEventBase> event; - if (context.SS->EnablePQConfigTransactionsAtSchemeShard) { - event = MakeEvProposeTransaction(OperationId.GetTxId(), - *pqGroup, - *pqShard, - topicName, - topicPath.PathString(), - bootstrapConfig, - cloudId, - folderId, - databaseId, - databasePath, - txState->TxType, - context); - } else { - event = MakeEvUpdateConfig(OperationId.GetTxId(), - *pqGroup, - *pqShard, - topicName, - topicPath.PathString(), - bootstrapConfig, - cloudId, - folderId, - databaseId, - databasePath, - txState->TxType, - context); - } - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "Propose configure PersQueue" - << ", opId: " << OperationId - << ", tabletId: " << tabletId - << ", at schemeshard: " << ssId); - - context.OnComplete.BindMsgToPipe(OperationId, tabletId, idx, event.Release()); - } else { - Y_ABORT_UNLESS(shard.TabletType == ETabletType::PersQueueReadBalancer); - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "Propose configure PersQueueReadBalancer" - << ", opId: " << OperationId - << ", tabletId: " << tabletId - << ", at schemeshard: " << ssId); - - pqGroup->BalancerTabletID = tabletId; - if (pqGroup->AlterData) { - pqGroup->AlterData->BalancerTabletID = tabletId; - } - - if (pqGroup->AlterData) { - pqGroup->AlterData->BalancerShardIdx = idx; - } - - TAutoPtr<TEvPersQueue::TEvUpdateBalancerConfig> event(new TEvPersQueue::TEvUpdateBalancerConfig()); - event->Record.SetTxId(ui64(OperationId.GetTxId())); - - ParsePQTabletConfig(*event->Record.MutableTabletConfig(), *pqGroup); - - Y_ABORT_UNLESS(pqGroup->AlterData); - - event->Record.SetTopicName(topicName); - event->Record.SetPathId(txState->TargetPathId.LocalPathId); - event->Record.SetPath(TPath::Init(txState->TargetPathId, context.SS).PathString()); - event->Record.SetPartitionPerTablet(pqGroup->AlterData ? pqGroup->AlterData->MaxPartsPerTablet : pqGroup->MaxPartsPerTablet); - event->Record.SetSchemeShardId(context.SS->TabletID()); - - event->Record.SetTotalGroupCount(pqGroup->AlterData ? pqGroup->AlterData->TotalGroupCount : pqGroup->TotalGroupCount); - event->Record.SetNextPartitionId(pqGroup->AlterData ? pqGroup->AlterData->NextPartitionId : pqGroup->NextPartitionId); - - event->Record.SetVersion(pqGroup->AlterData->AlterVersion); - - for (const auto& p : pqGroup->Shards) { - const auto& pqShard = p.second; - const auto& tabletId = context.SS->ShardInfos[p.first].TabletID; - auto tablet = event->Record.AddTablets(); - tablet->SetTabletId(ui64(tabletId)); - tablet->SetOwner(context.SS->TabletID()); - tablet->SetIdx(ui64(p.first.GetLocalId())); - for (const auto& pq : pqShard->Partitions) { - auto info = event->Record.AddPartitions(); - info->SetPartition(pq->PqId); - info->SetTabletId(ui64(tabletId)); - info->SetGroup(pq->GroupId); - info->SetCreateVersion(pq->CreateVersion); - - if (pq->KeyRange) { - pq->KeyRange->SerializeToProto(*info->MutableKeyRange()); - } - info->SetStatus(pq->Status); - info->MutableParentPartitionIds()->Reserve(pq->ParentPartitionIds.size()); - for (const auto parent : pq->ParentPartitionIds) { - info->MutableParentPartitionIds()->AddAlreadyReserved(parent); - } - info->MutableChildPartitionIds()->Reserve(pq->ChildPartitionIds.size()); - for (const auto children : pq->ChildPartitionIds) { - info->MutableChildPartitionIds()->AddAlreadyReserved(children); - } - } - } - - if (const ui64 subDomainPathId = context.SS->ResolvePathIdForDomain(txState->TargetPathId).LocalPathId) { - event->Record.SetSubDomainPathId(subDomainPathId); - } - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "Propose configure PersQueueReadBalancer" - << ", opId: " << OperationId - << ", tabletId: " << tabletId - << ", message: " << event->Record.ShortUtf8DebugString() - << ", at schemeshard: " << ssId); - - context.OnComplete.BindMsgToPipe(OperationId, tabletId, idx, event.Release()); - } - } - - txState->UpdateShardsInProgress(); - return false; - } - -private: - static void FillPartition(NKikimrPQ::TPQTabletConfig::TPartition& partition, const TTopicTabletInfo::TTopicPartitionInfo* pq, ui64 tabletId) { - partition.SetPartitionId(pq->PqId); - partition.SetCreateVersion(pq->CreateVersion); - if (pq->KeyRange) { - pq->KeyRange->SerializeToProto(*partition.MutableKeyRange()); - } - partition.SetStatus(pq->Status); - partition.MutableParentPartitionIds()->Reserve(pq->ParentPartitionIds.size()); - for (const auto parent : pq->ParentPartitionIds) { - partition.MutableParentPartitionIds()->AddAlreadyReserved(parent); - } - partition.MutableChildPartitionIds()->Reserve(pq->ChildPartitionIds.size()); - for (const auto children : pq->ChildPartitionIds) { - partition.MutableChildPartitionIds()->AddAlreadyReserved(children); - } - partition.SetTabletId(tabletId); - } - - static void MakePQTabletConfig(const TOperationContext& context, - NKikimrPQ::TPQTabletConfig& config, - const TTopicInfo& pqGroup, - const TTopicTabletInfo& pqShard, - const TString& topicName, - const TString& topicPath, - const TString& cloudId, - const TString& folderId, - const TString& databaseId, - const TString& databasePath) - { - ParsePQTabletConfig(config, pqGroup); - - config.SetTopicName(topicName); - config.SetTopicPath(topicPath); - config.MutablePartitionConfig()->SetTotalPartitions(pqGroup.AlterData ? pqGroup.AlterData->TotalGroupCount : pqGroup.TotalGroupCount); - - config.SetYcCloudId(cloudId); - config.SetYcFolderId(folderId); - config.SetYdbDatabaseId(databaseId); - config.SetYdbDatabasePath(databasePath); - - if (pqGroup.AlterData) { - config.SetVersion(pqGroup.AlterData->AlterVersion); - } - - THashSet<ui32> linkedPartitions; - - for(const auto& pq : pqShard.Partitions) { - config.AddPartitionIds(pq->PqId); - - auto& partition = *config.AddPartitions(); - FillPartition(partition, pq.Get(), 0); - - linkedPartitions.insert(pq->PqId); - linkedPartitions.insert(pq->ParentPartitionIds.begin(), pq->ParentPartitionIds.end()); - linkedPartitions.insert(pq->ChildPartitionIds.begin(), pq->ChildPartitionIds.end()); - for (auto c : pq->ChildPartitionIds) { - auto it = pqGroup.Partitions.find(c); - if (it == pqGroup.Partitions.end()) { - continue; - } - linkedPartitions.insert(it->second->ParentPartitionIds.begin(), it->second->ParentPartitionIds.end()); - } - } - - for(auto lp : linkedPartitions) { - auto it = pqGroup.Partitions.find(lp); - if (it == pqGroup.Partitions.end()) { - continue; - } - - auto* partitionInfo = it->second; - const auto& tabletId = context.SS->ShardInfos[partitionInfo->ShardIdx].TabletID; - - auto& partition = *config.AddAllPartitions(); - FillPartition(partition, partitionInfo, ui64(tabletId)); - } - } - - static void ParsePQTabletConfig(NKikimrPQ::TPQTabletConfig& config, - const TTopicInfo& pqGroup) - { - const TString* source = &pqGroup.TabletConfig; - if (pqGroup.AlterData) { - if (!pqGroup.AlterData->TabletConfig.empty()) - source = &pqGroup.AlterData->TabletConfig; - } - - if (!source->empty()) { - Y_ABORT_UNLESS(ParseFromStringNoSizeLimit(config, *source)); - } - } - - static THolder<TEvPersQueue::TEvProposeTransaction> - MakeEvProposeTransaction(TTxId txId, - const TTopicInfo& pqGroup, - const TTopicTabletInfo& pqShard, - const TString& topicName, - const TString& topicPath, - const std::optional<TBootstrapConfigWrapper>& bootstrapConfig, - const TString& cloudId, - const TString& folderId, - const TString& databaseId, - const TString& databasePath, - TTxState::ETxType txType, - const TOperationContext& context); - static THolder<TEvPersQueue::TEvUpdateConfig> - MakeEvUpdateConfig(TTxId txId, - const TTopicInfo& pqGroup, - const TTopicTabletInfo& pqShard, - const TString& topicName, - const TString& topicPath, - const std::optional<TBootstrapConfigWrapper>& bootstrapConfig, - const TString& cloudId, - const TString& folderId, - const TString& databaseId, - const TString& databasePath, - TTxState::ETxType txType, - const TOperationContext& context); + bool HandleReply(TEvPersQueue::TEvUpdateConfigResponse::TPtr& ev, TOperationContext& context) override; }; class TPropose: public TSubOperationState { @@ -1015,71 +152,12 @@ private: } public: - TPropose(TOperationId id) - : OperationId(id) - { - IgnoreMessages(DebugHint(), {TEvHive::TEvCreateTabletReply::EventType, TEvPersQueue::TEvUpdateConfigResponse::EventType}); - } + TPropose(TOperationId id); + bool ProgressState(TOperationContext& context) override; bool HandleReply(TEvPersQueue::TEvProposeTransactionResult::TPtr& ev, TOperationContext& context) override; bool HandleReply(TEvPersQueue::TEvProposeTransactionAttachResult::TPtr& ev, TOperationContext& context) override; - - bool HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) override { - TStepId step = TStepId(ev->Get()->StepId); - TTabletId ssId = context.SS->SelfTabletId(); - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() - << " HandleReply TEvOperationPlan" - << ", step: " << step - << ", at tablet: " << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreatePQGroup || txState->TxType == TTxState::TxAlterPQGroup); - - TPathId pathId = txState->TargetPathId; - TPathElement::TPtr path = context.SS->PathsById.at(pathId); - - NIceDb::TNiceDb db(context.GetDB()); - - if (path->StepCreated == InvalidStepId) { - path->StepCreated = step; - context.SS->PersistCreateStep(db, pathId, step); - } - - return TryPersistState(context); - } - - bool ProgressState(TOperationContext& context) override { - TTabletId ssId = context.SS->SelfTabletId(); - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "NPQState::TPropose ProgressState" - << ", operationId: " << OperationId - << ", at schemeshard: " << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreatePQGroup || txState->TxType == TTxState::TxAlterPQGroup); - - // - // If the program works according to the new scheme, then we must add PQ tablets to the list for - // the Coordinator. At this stage, we cannot rely on the value of - // the EnablePQConfigTransactionsAtSchemeShard flag. Because the operation could have started on one tablet - // and moved to another by that time. - // - // Therefore, here we check the value of the minStep field, which is filled in in - // the TEvProposeTransactionResult handler - // - TSet<TTabletId> shardSet; - if (ui64(txState->MinStep) > 0) { - PrepareShards(*txState, shardSet, context); - } - context.OnComplete.ProposeToCoordinator(OperationId, txState->TargetPathId, txState->MinStep, shardSet); - - return false; - } + bool HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) override; private: bool CanPersistState(const TTxState& txState, @@ -1111,122 +189,12 @@ private: } public: - TConfigureParts(TOperationId id) - : OperationId(id) - { - IgnoreMessages(DebugHint(), {TEvHive::TEvCreateTabletReply::EventType}); - } - - bool HandleReply(TEvBlockStore::TEvUpdateVolumeConfigResponse::TPtr& ev, TOperationContext& context) override { - TTabletId ssId = context.SS->SelfTabletId(); - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvSetConfigResult" - << ", at schemeshard: " << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateBlockStoreVolume || txState->TxType == TTxState::TxAlterBlockStoreVolume); - Y_ABORT_UNLESS(txState->State == TTxState::ConfigureParts); - - TTabletId tabletId = TTabletId(ev->Get()->Record.GetOrigin()); - NKikimrBlockStore::EStatus status = ev->Get()->Record.GetStatus(); - - // Schemeshard never sends invalid or outdated configs - Y_VERIFY_S(status == NKikimrBlockStore::OK || status == NKikimrBlockStore::ERROR_UPDATE_IN_PROGRESS, - "Unexpected error in UpdateVolumeConfigResponse," - << " status " << NKikimrBlockStore::EStatus_Name(status) - << " Tx " << OperationId - << " tablet " << tabletId); - - if (status == NKikimrBlockStore::ERROR_UPDATE_IN_PROGRESS) { - LOG_ERROR_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "BlockStore reconfiguration is in progress. We'll try to finish it later." - << " Tx " << OperationId - << " tablet " << tabletId); - return false; - } - - TShardIdx idx = context.SS->MustGetShardIdx(tabletId); - txState->ShardsInProgress.erase(idx); - - context.OnComplete.UnbindMsgFromPipe(OperationId, tabletId, idx); - - if (txState->ShardsInProgress.empty()) { - NIceDb::TNiceDb db(context.GetDB()); - context.SS->ChangeTxState(db, OperationId, TTxState::Propose); - context.OnComplete.ActivateTx(OperationId); - return true; - } - - return false; - } - - bool ProgressState(TOperationContext& context) override { - TTabletId ssId = context.SS->SelfTabletId(); - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " ProgressState" - << ", at schemeshard" << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateBlockStoreVolume || txState->TxType == TTxState::TxAlterBlockStoreVolume); - Y_ABORT_UNLESS(!txState->Shards.empty()); - - txState->ClearShardsInProgress(); - - TBlockStoreVolumeInfo::TPtr volume = context.SS->BlockStoreVolumes[txState->TargetPathId]; - Y_VERIFY_S(volume, "volume is null. PathId: " << txState->TargetPathId); - - ui64 version = volume->AlterVersion; - const auto* volumeConfig = &volume->VolumeConfig; - if (volume->AlterData) { - version = volume->AlterData->AlterVersion; - volumeConfig = &volume->AlterData->VolumeConfig; - } - - for (auto shard : txState->Shards) { - if (shard.TabletType == ETabletType::BlockStorePartition || - shard.TabletType == ETabletType::BlockStorePartition2) { - continue; - } - - Y_ABORT_UNLESS(shard.TabletType == ETabletType::BlockStoreVolume); - TShardIdx shardIdx = shard.Idx; - TTabletId tabletId = context.SS->ShardInfos[shardIdx].TabletID; - - volume->VolumeTabletId = tabletId; - if (volume->AlterData) { - volume->AlterData->VolumeTabletId = tabletId; - volume->AlterData->VolumeShardIdx = shardIdx; - } + TConfigureParts(TOperationId id); - TAutoPtr<TEvBlockStore::TEvUpdateVolumeConfig> event(new TEvBlockStore::TEvUpdateVolumeConfig()); - event->Record.SetTxId(ui64(OperationId.GetTxId())); - - event->Record.MutableVolumeConfig()->CopyFrom(*volumeConfig); - event->Record.MutableVolumeConfig()->SetVersion(version); - - for (const auto& p : volume->Shards) { - const auto& part = p.second; - const auto& partTabletId = context.SS->ShardInfos[p.first].TabletID; - auto info = event->Record.AddPartitions(); - info->SetPartitionId(part->PartitionId); - info->SetTabletId(ui64(partTabletId)); - } - - context.OnComplete.BindMsgToPipe(OperationId, tabletId, shardIdx, event.Release()); - - // Wait for results from this shard - txState->ShardsInProgress.insert(shardIdx); - } - - return false; - } + bool ProgressState(TOperationContext& context) override; + bool HandleReply(TEvBlockStore::TEvUpdateVolumeConfigResponse::TPtr& ev, TOperationContext& context) override; }; - class TPropose: public TSubOperationState { private: TOperationId OperationId; @@ -1238,79 +206,10 @@ private: } public: - TPropose(TOperationId id) - : OperationId(id) - { - IgnoreMessages(DebugHint(), {TEvHive::TEvCreateTabletReply::EventType, TEvBlockStore::TEvUpdateVolumeConfigResponse::EventType}); - } - - bool HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) override { - TStepId step = TStepId(ev->Get()->StepId); - TTabletId ssId = context.SS->SelfTabletId(); - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvOperationPlan" - << ", at schemeshard: " << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - if (!txState) { - return false; - } - Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateBlockStoreVolume || txState->TxType == TTxState::TxAlterBlockStoreVolume); - - TPathId pathId = txState->TargetPathId; - TPathElement::TPtr path = context.SS->PathsById.at(pathId); - - NIceDb::TNiceDb db(context.GetDB()); - - if (path->StepCreated == InvalidStepId) { - path->StepCreated = step; - context.SS->PersistCreateStep(db, pathId, step); - } - - TBlockStoreVolumeInfo::TPtr volume = context.SS->BlockStoreVolumes.at(pathId); - - auto oldVolumeSpace = volume->GetVolumeSpace(); - volume->FinishAlter(); - auto newVolumeSpace = volume->GetVolumeSpace(); - // Decrease in occupied space is applied on tx finish - auto domainDir = context.SS->PathsById.at(context.SS->ResolvePathIdForDomain(path)); - Y_ABORT_UNLESS(domainDir); - domainDir->ChangeVolumeSpaceCommit(newVolumeSpace, oldVolumeSpace); - - context.SS->PersistBlockStoreVolume(db, pathId, volume); - context.SS->PersistRemoveBlockStoreVolumeAlter(db, pathId); - - if (txState->TxType == TTxState::TxCreateBlockStoreVolume) { - auto parentDir = context.SS->PathsById.at(path->ParentPathId); - ++parentDir->DirAlterVersion; - context.SS->PersistPathDirAlterVersion(db, parentDir); - context.SS->ClearDescribePathCaches(parentDir); - context.OnComplete.PublishToSchemeBoard(OperationId, parentDir->PathId); - } + TPropose(TOperationId id); - context.SS->ClearDescribePathCaches(path); - context.OnComplete.PublishToSchemeBoard(OperationId, pathId); - - context.SS->ChangeTxState(db, OperationId, TTxState::Done); - return true; - } - - bool ProgressState(TOperationContext& context) override { - TTabletId ssId = context.SS->SelfTabletId(); - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " ProgressState" - << ", at schemeshard: " << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateBlockStoreVolume || txState->TxType == TTxState::TxAlterBlockStoreVolume); - - - context.OnComplete.ProposeToCoordinator(OperationId, txState->TargetPathId, TStepId(0)); - return false; - } + bool ProgressState(TOperationContext& context) override; + bool HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) override; }; } // NBSVState @@ -1324,77 +223,17 @@ class TConfigurePartsAtTable: public TSubOperationState { << " operationId: " << OperationId; } - static bool IsExpectedTxType(TTxState::ETxType txType) { - switch (txType) { - case TTxState::TxCreateCdcStreamAtTable: - case TTxState::TxCreateCdcStreamAtTableWithInitialScan: - case TTxState::TxAlterCdcStreamAtTable: - case TTxState::TxAlterCdcStreamAtTableDropSnapshot: - case TTxState::TxDropCdcStreamAtTable: - case TTxState::TxDropCdcStreamAtTableDropSnapshot: - return true; - default: - return false; - } - } - protected: virtual void FillNotice(const TPathId& pathId, NKikimrTxDataShard::TFlatSchemeTransaction& tx, TOperationContext& context) const = 0; public: - explicit TConfigurePartsAtTable(TOperationId id) - : OperationId(id) - { - IgnoreMessages(DebugHint(), {}); - } - - bool ProgressState(TOperationContext& context) override { - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " ProgressState" - << ", at schemeshard: " << context.SS->TabletID()); - - auto* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(IsExpectedTxType(txState->TxType)); - const auto& pathId = txState->TargetPathId; - - if (NTableState::CheckPartitioningChangedForTableModification(*txState, context)) { - NTableState::UpdatePartitioningForTableModification(OperationId, *txState, context); - } - - NKikimrTxDataShard::TFlatSchemeTransaction tx; - context.SS->FillSeqNo(tx, context.SS->StartRound(*txState)); - FillNotice(pathId, tx, context); - - txState->ClearShardsInProgress(); - Y_ABORT_UNLESS(txState->Shards.size()); - - for (ui32 i = 0; i < txState->Shards.size(); ++i) { - const auto& idx = txState->Shards[i].Idx; - const auto datashardId = context.SS->ShardInfos[idx].TabletID; - auto ev = context.SS->MakeDataShardProposal(pathId, OperationId, tx.SerializeAsString(), context.Ctx); - context.OnComplete.BindMsgToPipe(OperationId, datashardId, idx, ev.Release()); - } - - txState->UpdateShardsInProgress(TTxState::ConfigureParts); - return false; - } - - bool HandleReply(TEvDataShard::TEvProposeTransactionResult::TPtr& ev, TOperationContext& context) override { - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply " << ev->Get()->ToString() - << ", at schemeshard: " << context.SS->TabletID()); + explicit TConfigurePartsAtTable(TOperationId id); - if (!NTableState::CollectProposeTransactionResults(OperationId, ev, context)) { - return false; - } - - return true; - } + bool ProgressState(TOperationContext& context) override; + bool HandleReply(TEvDataShard::TEvProposeTransactionResult::TPtr& ev, TOperationContext& context) override; private: const TOperationId OperationId; - }; // TConfigurePartsAtTable class TProposeAtTable: public TSubOperationState { @@ -1404,131 +243,31 @@ class TProposeAtTable: public TSubOperationState { << " operationId: " << OperationId; } - static bool IsExpectedTxType(TTxState::ETxType txType) { - switch (txType) { - case TTxState::TxCreateCdcStreamAtTable: - case TTxState::TxCreateCdcStreamAtTableWithInitialScan: - case TTxState::TxAlterCdcStreamAtTable: - case TTxState::TxAlterCdcStreamAtTableDropSnapshot: - case TTxState::TxDropCdcStreamAtTable: - case TTxState::TxDropCdcStreamAtTableDropSnapshot: - return true; - default: - return false; - } - } - public: - explicit TProposeAtTable(TOperationId id) - : OperationId(id) - { - IgnoreMessages(DebugHint(), {TEvDataShard::TEvProposeTransactionResult::EventType}); - } - - bool ProgressState(TOperationContext& context) override { - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " ProgressState" - << ", at schemeshard: " << context.SS->TabletID()); - - const auto* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(IsExpectedTxType(txState->TxType)); - - TSet<TTabletId> shardSet; - for (const auto& shard : txState->Shards) { - Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shard.Idx)); - shardSet.insert(context.SS->ShardInfos.at(shard.Idx).TabletID); - } - - context.OnComplete.ProposeToCoordinator(OperationId, txState->TargetPathId, txState->MinStep, shardSet); - return false; - } - - bool HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) override { - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " HandleReply TEvOperationPlan" - << ", step: " << ev->Get()->StepId - << ", at schemeshard: " << context.SS->TabletID()); - - const auto* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(IsExpectedTxType(txState->TxType)); - const auto& pathId = txState->TargetPathId; - - Y_ABORT_UNLESS(context.SS->PathsById.contains(pathId)); - auto path = context.SS->PathsById.at(pathId); - - Y_ABORT_UNLESS(context.SS->Tables.contains(pathId)); - auto table = context.SS->Tables.at(pathId); - - table->AlterVersion += 1; + explicit TProposeAtTable(TOperationId id); - NIceDb::TNiceDb db(context.GetDB()); - context.SS->PersistTableAlterVersion(db, pathId, table); - - context.SS->ClearDescribePathCaches(path); - context.OnComplete.PublishToSchemeBoard(OperationId, pathId); - - context.SS->ChangeTxState(db, OperationId, TTxState::ProposedWaitParts); - return true; - } - - bool HandleReply(TEvDataShard::TEvSchemaChanged::TPtr& ev, TOperationContext& context) override { - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << " TEvDataShard::TEvSchemaChanged" - << " triggers early, save it" - << ", at schemeshard: " << context.SS->TabletID()); - - NTableState::CollectSchemaChanged(OperationId, ev, context); - return false; - } + bool ProgressState(TOperationContext& context) override; + bool HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) override; + bool HandleReply(TEvDataShard::TEvSchemaChanged::TPtr& ev, TOperationContext& context) override; protected: const TOperationId OperationId; - }; // TProposeAtTable class TProposeAtTableDropSnapshot: public TProposeAtTable { public: using TProposeAtTable::TProposeAtTable; - bool HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) override { - TProposeAtTable::HandleReply(ev, context); - - const auto* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - const auto& pathId = txState->TargetPathId; - - Y_ABORT_UNLESS(context.SS->TablesWithSnapshots.contains(pathId)); - const auto snapshotTxId = context.SS->TablesWithSnapshots.at(pathId); - - auto it = context.SS->SnapshotTables.find(snapshotTxId); - if (it != context.SS->SnapshotTables.end()) { - it->second.erase(pathId); - if (it->second.empty()) { - context.SS->SnapshotTables.erase(it); - } - } - - context.SS->SnapshotsStepIds.erase(snapshotTxId); - context.SS->TablesWithSnapshots.erase(pathId); - - NIceDb::TNiceDb db(context.GetDB()); - context.SS->PersistDropSnapshot(db, snapshotTxId, pathId); - - context.SS->TabletCounters->Simple()[COUNTER_SNAPSHOTS_COUNT].Sub(1); - return true; - } - + bool HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) override; }; // TProposeAtTableDropSnapshot } // NCdcStreamState namespace NForceDrop { -void ValidateNoTransactionOnPaths(TOperationId operationId, const THashSet<TPathId>& paths, TOperationContext& context); +void ValidateNoTransactionOnPaths(TOperationId operationId, const THashSet<TPathId>& paths, TOperationContext& context); void CollectShards(const THashSet<TPathId>& paths, TOperationId operationId, TTxState* txState, TOperationContext& context); + } // namespace NForceDrop -} // namespace NSchemeShard -} // namespace NKikimr +} // namespace NKikimr::NSchemeShard diff --git a/ydb/core/tx/schemeshard/schemeshard__operation_common_bsv.cpp b/ydb/core/tx/schemeshard/schemeshard__operation_common_bsv.cpp new file mode 100644 index 00000000000..f58c437f2c3 --- /dev/null +++ b/ydb/core/tx/schemeshard/schemeshard__operation_common_bsv.cpp @@ -0,0 +1,203 @@ +#include "schemeshard__operation_common.h" + +#include "schemeshard_private.h" +#include <ydb/core/base/hive.h> +#include <ydb/core/blockstore/core/blockstore.h> + + +namespace NKikimr::NSchemeShard::NBSVState { + +// NBSVState::TConfigureParts +// +TConfigureParts::TConfigureParts(TOperationId id) + : OperationId(id) +{ + IgnoreMessages(DebugHint(), {TEvHive::TEvCreateTabletReply::EventType}); +} + +bool TConfigureParts::HandleReply(TEvBlockStore::TEvUpdateVolumeConfigResponse::TPtr& ev, TOperationContext& context) { + TTabletId ssId = context.SS->SelfTabletId(); + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvSetConfigResult" + << ", at schemeshard: " << ssId); + + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateBlockStoreVolume || txState->TxType == TTxState::TxAlterBlockStoreVolume); + Y_ABORT_UNLESS(txState->State == TTxState::ConfigureParts); + + TTabletId tabletId = TTabletId(ev->Get()->Record.GetOrigin()); + NKikimrBlockStore::EStatus status = ev->Get()->Record.GetStatus(); + + // Schemeshard never sends invalid or outdated configs + Y_VERIFY_S(status == NKikimrBlockStore::OK || status == NKikimrBlockStore::ERROR_UPDATE_IN_PROGRESS, + "Unexpected error in UpdateVolumeConfigResponse," + << " status " << NKikimrBlockStore::EStatus_Name(status) + << " Tx " << OperationId + << " tablet " << tabletId); + + if (status == NKikimrBlockStore::ERROR_UPDATE_IN_PROGRESS) { + LOG_ERROR_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "BlockStore reconfiguration is in progress. We'll try to finish it later." + << " Tx " << OperationId + << " tablet " << tabletId); + return false; + } + + TShardIdx idx = context.SS->MustGetShardIdx(tabletId); + txState->ShardsInProgress.erase(idx); + + context.OnComplete.UnbindMsgFromPipe(OperationId, tabletId, idx); + + if (txState->ShardsInProgress.empty()) { + NIceDb::TNiceDb db(context.GetDB()); + context.SS->ChangeTxState(db, OperationId, TTxState::Propose); + context.OnComplete.ActivateTx(OperationId); + return true; + } + + return false; +} + +bool TConfigureParts::ProgressState(TOperationContext& context) { + TTabletId ssId = context.SS->SelfTabletId(); + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " ProgressState" + << ", at schemeshard" << ssId); + + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateBlockStoreVolume || txState->TxType == TTxState::TxAlterBlockStoreVolume); + Y_ABORT_UNLESS(!txState->Shards.empty()); + + txState->ClearShardsInProgress(); + + TBlockStoreVolumeInfo::TPtr volume = context.SS->BlockStoreVolumes[txState->TargetPathId]; + Y_VERIFY_S(volume, "volume is null. PathId: " << txState->TargetPathId); + + ui64 version = volume->AlterVersion; + const auto* volumeConfig = &volume->VolumeConfig; + if (volume->AlterData) { + version = volume->AlterData->AlterVersion; + volumeConfig = &volume->AlterData->VolumeConfig; + } + + for (auto shard : txState->Shards) { + if (shard.TabletType == ETabletType::BlockStorePartition || + shard.TabletType == ETabletType::BlockStorePartition2) { + continue; + } + + Y_ABORT_UNLESS(shard.TabletType == ETabletType::BlockStoreVolume); + TShardIdx shardIdx = shard.Idx; + TTabletId tabletId = context.SS->ShardInfos[shardIdx].TabletID; + + volume->VolumeTabletId = tabletId; + if (volume->AlterData) { + volume->AlterData->VolumeTabletId = tabletId; + volume->AlterData->VolumeShardIdx = shardIdx; + } + + TAutoPtr<TEvBlockStore::TEvUpdateVolumeConfig> event(new TEvBlockStore::TEvUpdateVolumeConfig()); + event->Record.SetTxId(ui64(OperationId.GetTxId())); + + event->Record.MutableVolumeConfig()->CopyFrom(*volumeConfig); + event->Record.MutableVolumeConfig()->SetVersion(version); + + for (const auto& p : volume->Shards) { + const auto& part = p.second; + const auto& partTabletId = context.SS->ShardInfos[p.first].TabletID; + auto info = event->Record.AddPartitions(); + info->SetPartitionId(part->PartitionId); + info->SetTabletId(ui64(partTabletId)); + } + + context.OnComplete.BindMsgToPipe(OperationId, tabletId, shardIdx, event.Release()); + + // Wait for results from this shard + txState->ShardsInProgress.insert(shardIdx); + } + + return false; +} + + +// NBSVState::TPropose +// +TPropose::TPropose(TOperationId id) + : OperationId(id) +{ + IgnoreMessages(DebugHint(), {TEvHive::TEvCreateTabletReply::EventType, TEvBlockStore::TEvUpdateVolumeConfigResponse::EventType}); +} + +bool TPropose::HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) { + TStepId step = TStepId(ev->Get()->StepId); + TTabletId ssId = context.SS->SelfTabletId(); + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvOperationPlan" + << ", at schemeshard: " << ssId); + + TTxState* txState = context.SS->FindTx(OperationId); + if (!txState) { + return false; + } + Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateBlockStoreVolume || txState->TxType == TTxState::TxAlterBlockStoreVolume); + + TPathId pathId = txState->TargetPathId; + TPathElement::TPtr path = context.SS->PathsById.at(pathId); + + NIceDb::TNiceDb db(context.GetDB()); + + if (path->StepCreated == InvalidStepId) { + path->StepCreated = step; + context.SS->PersistCreateStep(db, pathId, step); + } + + TBlockStoreVolumeInfo::TPtr volume = context.SS->BlockStoreVolumes.at(pathId); + + auto oldVolumeSpace = volume->GetVolumeSpace(); + volume->FinishAlter(); + auto newVolumeSpace = volume->GetVolumeSpace(); + // Decrease in occupied space is applied on tx finish + auto domainDir = context.SS->PathsById.at(context.SS->ResolvePathIdForDomain(path)); + Y_ABORT_UNLESS(domainDir); + domainDir->ChangeVolumeSpaceCommit(newVolumeSpace, oldVolumeSpace); + + context.SS->PersistBlockStoreVolume(db, pathId, volume); + context.SS->PersistRemoveBlockStoreVolumeAlter(db, pathId); + + if (txState->TxType == TTxState::TxCreateBlockStoreVolume) { + auto parentDir = context.SS->PathsById.at(path->ParentPathId); + ++parentDir->DirAlterVersion; + context.SS->PersistPathDirAlterVersion(db, parentDir); + context.SS->ClearDescribePathCaches(parentDir); + context.OnComplete.PublishToSchemeBoard(OperationId, parentDir->PathId); + } + + context.SS->ClearDescribePathCaches(path); + context.OnComplete.PublishToSchemeBoard(OperationId, pathId); + + context.SS->ChangeTxState(db, OperationId, TTxState::Done); + return true; +} + +bool TPropose::ProgressState(TOperationContext& context) { + TTabletId ssId = context.SS->SelfTabletId(); + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " ProgressState" + << ", at schemeshard: " << ssId); + + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateBlockStoreVolume || txState->TxType == TTxState::TxAlterBlockStoreVolume); + + + context.OnComplete.ProposeToCoordinator(OperationId, txState->TargetPathId, TStepId(0)); + return false; +} + +} // NKikimr::NSchemeShard::NBSVState diff --git a/ydb/core/tx/schemeshard/schemeshard__operation_common_cdc_stream.cpp b/ydb/core/tx/schemeshard/schemeshard__operation_common_cdc_stream.cpp new file mode 100644 index 00000000000..6c9d3b9058e --- /dev/null +++ b/ydb/core/tx/schemeshard/schemeshard__operation_common_cdc_stream.cpp @@ -0,0 +1,179 @@ +#include "schemeshard__operation_common.h" + +#include "schemeshard_private.h" +#include <ydb/core/base/hive.h> +#include <ydb/core/tx/datashard/datashard.h> + + +namespace NKikimr::NSchemeShard::NCdcStreamState { + +namespace { + +bool IsExpectedTxType(TTxState::ETxType txType) { + switch (txType) { + case TTxState::TxCreateCdcStreamAtTable: + case TTxState::TxCreateCdcStreamAtTableWithInitialScan: + case TTxState::TxAlterCdcStreamAtTable: + case TTxState::TxAlterCdcStreamAtTableDropSnapshot: + case TTxState::TxDropCdcStreamAtTable: + case TTxState::TxDropCdcStreamAtTableDropSnapshot: + return true; + default: + return false; + } +} + +} // namespace anonymous + + +// NCdcStreamState::TConfigurePartsAtTable +// +TConfigurePartsAtTable::TConfigurePartsAtTable(TOperationId id) + : OperationId(id) +{ + IgnoreMessages(DebugHint(), {}); +} + +bool TConfigurePartsAtTable::ProgressState(TOperationContext& context) { + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " ProgressState" + << ", at schemeshard: " << context.SS->SelfTabletId()); + + auto* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(IsExpectedTxType(txState->TxType)); + const auto& pathId = txState->TargetPathId; + + if (NTableState::CheckPartitioningChangedForTableModification(*txState, context)) { + NTableState::UpdatePartitioningForTableModification(OperationId, *txState, context); + } + + NKikimrTxDataShard::TFlatSchemeTransaction tx; + context.SS->FillSeqNo(tx, context.SS->StartRound(*txState)); + FillNotice(pathId, tx, context); + + txState->ClearShardsInProgress(); + Y_ABORT_UNLESS(txState->Shards.size()); + + for (ui32 i = 0; i < txState->Shards.size(); ++i) { + const auto& idx = txState->Shards[i].Idx; + const auto datashardId = context.SS->ShardInfos[idx].TabletID; + auto ev = context.SS->MakeDataShardProposal(pathId, OperationId, tx.SerializeAsString(), context.Ctx); + context.OnComplete.BindMsgToPipe(OperationId, datashardId, idx, ev.Release()); + } + + txState->UpdateShardsInProgress(TTxState::ConfigureParts); + return false; +} + +bool TConfigurePartsAtTable::HandleReply(TEvDataShard::TEvProposeTransactionResult::TPtr& ev, TOperationContext& context) { + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply " << ev->Get()->ToString() + << ", at schemeshard: " << context.SS->SelfTabletId()); + + if (!NTableState::CollectProposeTransactionResults(OperationId, ev, context)) { + return false; + } + + return true; +} + + +// NCdcStreamState::TProposeAtTable +// +TProposeAtTable::TProposeAtTable(TOperationId id) + : OperationId(id) +{ + IgnoreMessages(DebugHint(), {TEvDataShard::TEvProposeTransactionResult::EventType}); +} + +bool TProposeAtTable::ProgressState(TOperationContext& context) { + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " ProgressState" + << ", at schemeshard: " << context.SS->SelfTabletId()); + + const auto* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(IsExpectedTxType(txState->TxType)); + + TSet<TTabletId> shardSet; + for (const auto& shard : txState->Shards) { + Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shard.Idx)); + shardSet.insert(context.SS->ShardInfos.at(shard.Idx).TabletID); + } + + context.OnComplete.ProposeToCoordinator(OperationId, txState->TargetPathId, txState->MinStep, shardSet); + return false; +} + +bool TProposeAtTable::HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) { + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvOperationPlan" + << ", step: " << ev->Get()->StepId + << ", at schemeshard: " << context.SS->SelfTabletId()); + + const auto* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(IsExpectedTxType(txState->TxType)); + const auto& pathId = txState->TargetPathId; + + Y_ABORT_UNLESS(context.SS->PathsById.contains(pathId)); + auto path = context.SS->PathsById.at(pathId); + + Y_ABORT_UNLESS(context.SS->Tables.contains(pathId)); + auto table = context.SS->Tables.at(pathId); + + table->AlterVersion += 1; + + NIceDb::TNiceDb db(context.GetDB()); + context.SS->PersistTableAlterVersion(db, pathId, table); + + context.SS->ClearDescribePathCaches(path); + context.OnComplete.PublishToSchemeBoard(OperationId, pathId); + + context.SS->ChangeTxState(db, OperationId, TTxState::ProposedWaitParts); + return true; +} + +bool TProposeAtTable::HandleReply(TEvDataShard::TEvSchemaChanged::TPtr& ev, TOperationContext& context) { + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " TEvDataShard::TEvSchemaChanged" + << " triggers early, save it" + << ", at schemeshard: " << context.SS->SelfTabletId()); + + NTableState::CollectSchemaChanged(OperationId, ev, context); + return false; +} + + +// NCdcStreamState::TProposeAtTableDropSnapshot +// +bool TProposeAtTableDropSnapshot::HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) { + TProposeAtTable::HandleReply(ev, context); + + const auto* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + const auto& pathId = txState->TargetPathId; + + Y_ABORT_UNLESS(context.SS->TablesWithSnapshots.contains(pathId)); + const auto snapshotTxId = context.SS->TablesWithSnapshots.at(pathId); + + auto it = context.SS->SnapshotTables.find(snapshotTxId); + if (it != context.SS->SnapshotTables.end()) { + it->second.erase(pathId); + if (it->second.empty()) { + context.SS->SnapshotTables.erase(it); + } + } + + context.SS->SnapshotsStepIds.erase(snapshotTxId); + context.SS->TablesWithSnapshots.erase(pathId); + + NIceDb::TNiceDb db(context.GetDB()); + context.SS->PersistDropSnapshot(db, snapshotTxId, pathId); + + context.SS->TabletCounters->Simple()[COUNTER_SNAPSHOTS_COUNT].Sub(1); + return true; +} + +} // NKikimr::NSchemeShard::NCdcStreamState diff --git a/ydb/core/tx/schemeshard/schemeshard__operation_common_pq.cpp b/ydb/core/tx/schemeshard/schemeshard__operation_common_pq.cpp new file mode 100644 index 00000000000..004a9af3e15 --- /dev/null +++ b/ydb/core/tx/schemeshard/schemeshard__operation_common_pq.cpp @@ -0,0 +1,814 @@ +#include "schemeshard__operation_common.h" + +#include <ydb/core/persqueue/writer/source_id_encoding.h> + +#include "schemeshard_private.h" +#include <ydb/core/base/hive.h> +#include <ydb/core/persqueue/events/global.h> + + +namespace NKikimr::NSchemeShard::NPQState { + +namespace { + +void ParsePQTabletConfig(NKikimrPQ::TPQTabletConfig& config, + const TTopicInfo& pqGroup) +{ + const TString* source = &pqGroup.TabletConfig; + if (pqGroup.AlterData) { + if (!pqGroup.AlterData->TabletConfig.empty()) + source = &pqGroup.AlterData->TabletConfig; + } + + if (!source->empty()) { + Y_ABORT_UNLESS(ParseFromStringNoSizeLimit(config, *source)); + } +} + +void FillPartition(NKikimrPQ::TPQTabletConfig::TPartition& partition, const TTopicTabletInfo::TTopicPartitionInfo* pq, ui64 tabletId) { + partition.SetPartitionId(pq->PqId); + partition.SetCreateVersion(pq->CreateVersion); + if (pq->KeyRange) { + pq->KeyRange->SerializeToProto(*partition.MutableKeyRange()); + } + partition.SetStatus(pq->Status); + partition.MutableParentPartitionIds()->Reserve(pq->ParentPartitionIds.size()); + for (const auto parent : pq->ParentPartitionIds) { + partition.MutableParentPartitionIds()->AddAlreadyReserved(parent); + } + partition.MutableChildPartitionIds()->Reserve(pq->ChildPartitionIds.size()); + for (const auto children : pq->ChildPartitionIds) { + partition.MutableChildPartitionIds()->AddAlreadyReserved(children); + } + partition.SetTabletId(tabletId); +} + +void MakePQTabletConfig(const TOperationContext& context, + NKikimrPQ::TPQTabletConfig& config, + const TTopicInfo& pqGroup, + const TTopicTabletInfo& pqShard, + const TString& topicName, + const TString& topicPath, + const TString& cloudId, + const TString& folderId, + const TString& databaseId, + const TString& databasePath) +{ + ParsePQTabletConfig(config, pqGroup); + + config.SetTopicName(topicName); + config.SetTopicPath(topicPath); + config.MutablePartitionConfig()->SetTotalPartitions(pqGroup.AlterData ? pqGroup.AlterData->TotalGroupCount : pqGroup.TotalGroupCount); + + config.SetYcCloudId(cloudId); + config.SetYcFolderId(folderId); + config.SetYdbDatabaseId(databaseId); + config.SetYdbDatabasePath(databasePath); + + if (pqGroup.AlterData) { + config.SetVersion(pqGroup.AlterData->AlterVersion); + } + + THashSet<ui32> linkedPartitions; + + for(const auto& pq : pqShard.Partitions) { + config.AddPartitionIds(pq->PqId); + + auto& partition = *config.AddPartitions(); + FillPartition(partition, pq.Get(), 0); + + linkedPartitions.insert(pq->PqId); + linkedPartitions.insert(pq->ParentPartitionIds.begin(), pq->ParentPartitionIds.end()); + linkedPartitions.insert(pq->ChildPartitionIds.begin(), pq->ChildPartitionIds.end()); + for (auto c : pq->ChildPartitionIds) { + auto it = pqGroup.Partitions.find(c); + if (it == pqGroup.Partitions.end()) { + continue; + } + linkedPartitions.insert(it->second->ParentPartitionIds.begin(), it->second->ParentPartitionIds.end()); + } + } + + for(auto lp : linkedPartitions) { + auto it = pqGroup.Partitions.find(lp); + if (it == pqGroup.Partitions.end()) { + continue; + } + + auto* partitionInfo = it->second; + const auto& tabletId = context.SS->ShardInfos[partitionInfo->ShardIdx].TabletID; + + auto& partition = *config.AddAllPartitions(); + FillPartition(partition, partitionInfo, ui64(tabletId)); + } +} + +class TBootstrapConfigWrapper: public NKikimrPQ::TBootstrapConfig { + struct TSerializedProposeTransaction { + TString Value; + + static TSerializedProposeTransaction Serialize(const NKikimrPQ::TBootstrapConfig& value) { + NKikimrPQ::TEvProposeTransaction record; + record.MutableConfig()->MutableBootstrapConfig()->CopyFrom(value); + return {record.SerializeAsString()}; + } + }; + + struct TSerializedUpdateConfig { + TString Value; + + static TSerializedUpdateConfig Serialize(const NKikimrPQ::TBootstrapConfig& value) { + NKikimrPQ::TUpdateConfig record; + record.MutableBootstrapConfig()->CopyFrom(value); + return {record.SerializeAsString()}; + } + }; + + mutable std::optional<std::variant< + TSerializedProposeTransaction, + TSerializedUpdateConfig + >> PreSerialized; + + template <typename T> + const TString& Get() const { + if (!PreSerialized) { + PreSerialized.emplace(T::Serialize(*this)); + } + + const auto* value = std::get_if<T>(&PreSerialized.value()); + Y_ABORT_UNLESS(value); + + return value->Value; + } + +public: + const TString& GetPreSerializedProposeTransaction() const { + return Get<TSerializedProposeTransaction>(); + } + + const TString& GetPreSerializedUpdateConfig() const { + return Get<TSerializedUpdateConfig>(); + } +}; + +THolder<TEvPersQueue::TEvProposeTransaction> MakeEvProposeTransaction( + TTxId txId, + const TTopicInfo& pqGroup, + const TTopicTabletInfo& pqShard, + const TString& topicName, + const TString& topicPath, + const std::optional<TBootstrapConfigWrapper>& bootstrapConfig, + const TString& cloudId, + const TString& folderId, + const TString& databaseId, + const TString& databasePath, + TTxState::ETxType txType, + const TOperationContext& context + ) +{ + auto event = MakeHolder<TEvPersQueue::TEvProposeTransactionBuilder>(); + event->Record.SetTxId(ui64(txId)); + ActorIdToProto(context.SS->SelfId(), event->Record.MutableSourceActor()); + + MakePQTabletConfig(context, + *event->Record.MutableConfig()->MutableTabletConfig(), + pqGroup, + pqShard, + topicName, + topicPath, + cloudId, + folderId, + databaseId, + databasePath); + if (bootstrapConfig) { + Y_ABORT_UNLESS(txType == TTxState::TxCreatePQGroup); + event->PreSerializedData += bootstrapConfig->GetPreSerializedProposeTransaction(); + } + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "Propose configure PersQueue" << + ", message: " << event->Record.ShortUtf8DebugString()); + + return event; +} + +THolder<TEvPersQueue::TEvUpdateConfig> MakeEvUpdateConfig( + TTxId txId, + const TTopicInfo& pqGroup, + const TTopicTabletInfo& pqShard, + const TString& topicName, + const TString& topicPath, + const std::optional<TBootstrapConfigWrapper>& bootstrapConfig, + const TString& cloudId, + const TString& folderId, + const TString& databaseId, + const TString& databasePath, + TTxState::ETxType txType, + const TOperationContext& context + ) +{ + auto event = MakeHolder<TEvPersQueue::TEvUpdateConfigBuilder>(); + event->Record.SetTxId(ui64(txId)); + + MakePQTabletConfig(context, + *event->Record.MutableTabletConfig(), + pqGroup, + pqShard, + topicName, + topicPath, + cloudId, + folderId, + databaseId, + databasePath); + if (bootstrapConfig) { + Y_ABORT_UNLESS(txType == TTxState::TxCreatePQGroup); + event->PreSerializedData += bootstrapConfig->GetPreSerializedUpdateConfig(); + } + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "Propose configure PersQueue" << + ", message: " << event->Record.ShortUtf8DebugString()); + + return event; +} + +} // anonymous namespace + + +bool CollectPQConfigChanged(const TOperationId& operationId, + const TEvPersQueue::TEvProposeTransactionResult::TPtr& ev, + TOperationContext& context) +{ + Y_ABORT_UNLESS(context.SS->FindTx(operationId)); + TTxState& txState = *context.SS->FindTx(operationId); + + const auto& evRecord = ev->Get()->Record; + if (evRecord.GetStatus() == NKikimrPQ::TEvProposeTransactionResult::COMPLETE) { + const auto ssId = context.SS->SelfTabletId(); + const TTabletId shardId(evRecord.GetOrigin()); + + const auto shardIdx = context.SS->MustGetShardIdx(shardId); + Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shardIdx)); + + Y_ABORT_UNLESS(txState.State == TTxState::Propose); + + txState.ShardsInProgress.erase(shardIdx); + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "CollectPQConfigChanged accept TEvPersQueue::TEvProposeTransactionResult" + << ", operationId: " << operationId + << ", shardIdx: " << shardIdx + << ", shard: " << shardId + << ", left await: " << txState.ShardsInProgress.size() + << ", txState.State: " << TTxState::StateName(txState.State) + << ", txState.ReadyForNotifications: " << txState.ReadyForNotifications + << ", at schemeshard: " << ssId); + } + + return txState.ShardsInProgress.empty(); +} + +bool CollectPQConfigChanged(const TOperationId& operationId, + const TEvPersQueue::TEvProposeTransactionAttachResult::TPtr& ev, + TOperationContext& context) +{ + Y_ABORT_UNLESS(context.SS->FindTx(operationId)); + TTxState& txState = *context.SS->FindTx(operationId); + + // + // The PQ tablet can perform a transaction and send a TEvProposeTransactionResult(COMPLETE) response. + // The SchemeShard tablet can restart at this point. After restarting at the TPropose step, it will + // send the TEvProposeTransactionAttach message to the PQ tablets. If the NODATA status is specified in + // the response TEvProposeTransactionAttachResult, then the PQ tablet has already completed the transaction. + // Otherwise, she continues to execute the transaction + // + + const auto& evRecord = ev->Get()->Record; + if (evRecord.GetStatus() != NKikimrProto::NODATA) { + // + // If the PQ tablet returned something other than NODATA, then it continues to execute the transaction + // + return txState.ShardsInProgress.empty(); + } + + // + // Otherwise, she has already completed the transaction and has forgotten about it. Then we can + // remove PQ tablet from the list of shards + // + + const auto ssId = context.SS->SelfTabletId(); + const TTabletId shardId(evRecord.GetTabletId()); + + const auto shardIdx = context.SS->MustGetShardIdx(shardId); + Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shardIdx)); + + Y_ABORT_UNLESS(txState.State == TTxState::Propose); + + txState.ShardsInProgress.erase(shardIdx); + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "CollectPQConfigChanged accept TEvPersQueue::TEvProposeTransactionAttachResult" + << ", operationId: " << operationId + << ", shardIdx: " << shardIdx + << ", shard: " << shardId + << ", left await: " << txState.ShardsInProgress.size() + << ", txState.State: " << TTxState::StateName(txState.State) + << ", txState.ReadyForNotifications: " << txState.ReadyForNotifications + << ", at schemeshard: " << ssId); + + return txState.ShardsInProgress.empty(); +} + + +// NPQState::TConfigureParts +// +TConfigureParts::TConfigureParts(TOperationId id) + : OperationId(id) +{ + IgnoreMessages(DebugHint(), {TEvHive::TEvCreateTabletReply::EventType}); +} + +bool TConfigureParts::HandleReply(TEvPersQueue::TEvProposeTransactionResult::TPtr& ev, TOperationContext& context) +{ + const TTabletId ssId = context.SS->SelfTabletId(); + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvProposeTransactionResult" + << ", at schemeshard: " << ssId); + + return NPQState::CollectProposeTransactionResults(OperationId, ev, context); +} + +bool TConfigureParts::HandleReply(TEvPersQueue::TEvUpdateConfigResponse::TPtr& ev, TOperationContext& context) { + TTabletId ssId = context.SS->SelfTabletId(); + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvUpdateConfigResponse" + << " at tablet" << ssId); + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvUpdateConfigResponse" + << " message: " << ev->Get()->Record.ShortUtf8DebugString() + << " at tablet" << ssId); + + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreatePQGroup || txState->TxType == TTxState::TxAlterPQGroup); + + TTabletId tabletId = TTabletId(ev->Get()->Record.GetOrigin()); + NKikimrPQ::EStatus status = ev->Get()->Record.GetStatus(); + + // Schemeshard always sends a valid config to the PQ tablet and PQ tablet is not supposed to reject it + // Also Schemeshard never send multiple different config updates simultaneously (same config + // can be sent more than once in case of retries due to restarts or disconnects) + // If PQ tablet is not able to save a valid config it should kill itself and restart without + // sending error response + Y_VERIFY_S(status == NKikimrPQ::OK || status == NKikimrPQ::ERROR_UPDATE_IN_PROGRESS, + "Unexpected error in UpdateConfigResponse," + << " status: " << NKikimrPQ::EStatus_Name(status) + << " Tx " << OperationId + << " tablet "<< tabletId + << " at schemeshard: " << ssId); + + if (status == NKikimrPQ::ERROR_UPDATE_IN_PROGRESS) { + LOG_ERROR_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "PQ reconfiguration is in progress. We'll try to finish it later." + << " Tx " << OperationId + << " tablet " << tabletId + << " at schemeshard: " << ssId); + return false; + } + + Y_ABORT_UNLESS(txState->State == TTxState::ConfigureParts); + + TShardIdx idx = context.SS->MustGetShardIdx(tabletId); + txState->ShardsInProgress.erase(idx); + + // Detach datashard pipe + context.OnComplete.UnbindMsgFromPipe(OperationId, tabletId, idx); + + if (txState->ShardsInProgress.empty()) { + NIceDb::TNiceDb db(context.GetDB()); + context.SS->ChangeTxState(db, OperationId, TTxState::Propose); + return true; + } + + return false; +} + +bool TConfigureParts::ProgressState(TOperationContext& context) { + TTabletId ssId = context.SS->SelfTabletId(); + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() + << " HandleReply ProgressState" + << ", at schemeshard: " << ssId); + + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreatePQGroup || txState->TxType == TTxState::TxAlterPQGroup); + + txState->ClearShardsInProgress(); + + TString topicName = context.SS->PathsById.at(txState->TargetPathId)->Name; + Y_VERIFY_S(topicName.size(), + "topicName is empty" + <<", pathId: " << txState->TargetPathId); + + TTopicInfo::TPtr pqGroup = context.SS->Topics[txState->TargetPathId]; + Y_VERIFY_S(pqGroup, + "pqGroup is null" + << ", pathId " << txState->TargetPathId); + + const TPathElement::TPtr dbRootEl = context.SS->PathsById.at(context.SS->RootPathId()); + TString cloudId; + if (dbRootEl->UserAttrs->Attrs.contains("cloud_id")) { + cloudId = dbRootEl->UserAttrs->Attrs.at("cloud_id"); + } + TString folderId; + if (dbRootEl->UserAttrs->Attrs.contains("folder_id")) { + folderId = dbRootEl->UserAttrs->Attrs.at("folder_id"); + } + TString databaseId; + if (dbRootEl->UserAttrs->Attrs.contains("database_id")) { + databaseId = dbRootEl->UserAttrs->Attrs.at("database_id"); + } + + TString databasePath = TPath::Init(context.SS->RootPathId(), context.SS).PathString(); + auto topicPath = TPath::Init(txState->TargetPathId, context.SS); + + std::optional<TBootstrapConfigWrapper> bootstrapConfig; + if (txState->TxType == TTxState::TxCreatePQGroup && topicPath.Parent().IsCdcStream()) { + bootstrapConfig.emplace(); + + auto tablePath = topicPath.Parent().Parent(); // table/cdc_stream/topic + Y_ABORT_UNLESS(tablePath.IsResolved()); + + Y_ABORT_UNLESS(context.SS->Tables.contains(tablePath.Base()->PathId)); + auto table = context.SS->Tables.at(tablePath.Base()->PathId); + + const auto& partitions = table->GetPartitions(); + + for (ui32 i = 0; i < partitions.size(); ++i) { + const auto& cur = partitions.at(i); + + Y_ABORT_UNLESS(context.SS->ShardInfos.contains(cur.ShardIdx)); + const auto& shard = context.SS->ShardInfos.at(cur.ShardIdx); + + auto& mg = *bootstrapConfig->AddExplicitMessageGroups(); + mg.SetId(NPQ::NSourceIdEncoding::EncodeSimple(ToString(shard.TabletID))); + + if (i != partitions.size() - 1) { + mg.MutableKeyRange()->SetToBound(cur.EndOfRange); + } + + if (i) { + const auto& prev = partitions.at(i - 1); + mg.MutableKeyRange()->SetFromBound(prev.EndOfRange); + } + } + } + + for (auto shard : txState->Shards) { + TShardIdx idx = shard.Idx; + TTabletId tabletId = context.SS->ShardInfos.at(idx).TabletID; + + if (shard.TabletType == ETabletType::PersQueue) { + TTopicTabletInfo::TPtr pqShard = pqGroup->Shards.at(idx); + Y_VERIFY_S(pqShard, "pqShard is null, idx is " << idx << " has was "<< THash<TShardIdx>()(idx)); + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "Propose configure PersQueue" + << ", opId: " << OperationId + << ", tabletId: " << tabletId + << ", Partitions size: " << pqShard->Partitions.size() + << ", at schemeshard: " << ssId); + + THolder<NActors::IEventBase> event; + if (context.SS->EnablePQConfigTransactionsAtSchemeShard) { + event = MakeEvProposeTransaction(OperationId.GetTxId(), + *pqGroup, + *pqShard, + topicName, + topicPath.PathString(), + bootstrapConfig, + cloudId, + folderId, + databaseId, + databasePath, + txState->TxType, + context); + } else { + event = MakeEvUpdateConfig(OperationId.GetTxId(), + *pqGroup, + *pqShard, + topicName, + topicPath.PathString(), + bootstrapConfig, + cloudId, + folderId, + databaseId, + databasePath, + txState->TxType, + context); + } + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "Propose configure PersQueue" + << ", opId: " << OperationId + << ", tabletId: " << tabletId + << ", at schemeshard: " << ssId); + + context.OnComplete.BindMsgToPipe(OperationId, tabletId, idx, event.Release()); + } else { + Y_ABORT_UNLESS(shard.TabletType == ETabletType::PersQueueReadBalancer); + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "Propose configure PersQueueReadBalancer" + << ", opId: " << OperationId + << ", tabletId: " << tabletId + << ", at schemeshard: " << ssId); + + pqGroup->BalancerTabletID = tabletId; + if (pqGroup->AlterData) { + pqGroup->AlterData->BalancerTabletID = tabletId; + } + + if (pqGroup->AlterData) { + pqGroup->AlterData->BalancerShardIdx = idx; + } + + TAutoPtr<TEvPersQueue::TEvUpdateBalancerConfig> event(new TEvPersQueue::TEvUpdateBalancerConfig()); + event->Record.SetTxId(ui64(OperationId.GetTxId())); + + ParsePQTabletConfig(*event->Record.MutableTabletConfig(), *pqGroup); + + Y_ABORT_UNLESS(pqGroup->AlterData); + + event->Record.SetTopicName(topicName); + event->Record.SetPathId(txState->TargetPathId.LocalPathId); + event->Record.SetPath(TPath::Init(txState->TargetPathId, context.SS).PathString()); + event->Record.SetPartitionPerTablet(pqGroup->AlterData ? pqGroup->AlterData->MaxPartsPerTablet : pqGroup->MaxPartsPerTablet); + event->Record.SetSchemeShardId(ui64(context.SS->SelfTabletId())); + + event->Record.SetTotalGroupCount(pqGroup->AlterData ? pqGroup->AlterData->TotalGroupCount : pqGroup->TotalGroupCount); + event->Record.SetNextPartitionId(pqGroup->AlterData ? pqGroup->AlterData->NextPartitionId : pqGroup->NextPartitionId); + + event->Record.SetVersion(pqGroup->AlterData->AlterVersion); + + for (const auto& p : pqGroup->Shards) { + const auto& pqShard = p.second; + const auto& tabletId = context.SS->ShardInfos[p.first].TabletID; + auto tablet = event->Record.AddTablets(); + tablet->SetTabletId(ui64(tabletId)); + tablet->SetOwner(ui64(context.SS->SelfTabletId())); + tablet->SetIdx(ui64(p.first.GetLocalId())); + for (const auto& pq : pqShard->Partitions) { + auto info = event->Record.AddPartitions(); + info->SetPartition(pq->PqId); + info->SetTabletId(ui64(tabletId)); + info->SetGroup(pq->GroupId); + info->SetCreateVersion(pq->CreateVersion); + + if (pq->KeyRange) { + pq->KeyRange->SerializeToProto(*info->MutableKeyRange()); + } + info->SetStatus(pq->Status); + info->MutableParentPartitionIds()->Reserve(pq->ParentPartitionIds.size()); + for (const auto parent : pq->ParentPartitionIds) { + info->MutableParentPartitionIds()->AddAlreadyReserved(parent); + } + info->MutableChildPartitionIds()->Reserve(pq->ChildPartitionIds.size()); + for (const auto children : pq->ChildPartitionIds) { + info->MutableChildPartitionIds()->AddAlreadyReserved(children); + } + } + } + + if (const ui64 subDomainPathId = context.SS->ResolvePathIdForDomain(txState->TargetPathId).LocalPathId) { + event->Record.SetSubDomainPathId(subDomainPathId); + } + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "Propose configure PersQueueReadBalancer" + << ", opId: " << OperationId + << ", tabletId: " << tabletId + << ", message: " << event->Record.ShortUtf8DebugString() + << ", at schemeshard: " << ssId); + + context.OnComplete.BindMsgToPipe(OperationId, tabletId, idx, event.Release()); + } + } + + txState->UpdateShardsInProgress(); + return false; +} + + +// NPQState::TPropose +// +TPropose::TPropose(TOperationId id) + : OperationId(id) +{ + IgnoreMessages(DebugHint(), {TEvHive::TEvCreateTabletReply::EventType, TEvPersQueue::TEvUpdateConfigResponse::EventType}); +} + +bool TPropose::HandleReply(TEvPersQueue::TEvProposeTransactionResult::TPtr& ev, TOperationContext& context) +{ + const TTabletId ssId = context.SS->SelfTabletId(); + const auto& evRecord = ev->Get()->Record; + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvProposeTransactionResult" + << " triggers early" + << ", at schemeshard: " << ssId + << " message# " << evRecord.ShortDebugString()); + + const bool collected = CollectPQConfigChanged(OperationId, ev, context); + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvProposeTransactionResult" + << " CollectPQConfigChanged: " << (collected ? "true" : "false")); + + return TryPersistState(context); +} + +bool TPropose::HandleReply(TEvPersQueue::TEvProposeTransactionAttachResult::TPtr& ev, TOperationContext& context) +{ + const auto ssId = context.SS->SelfTabletId(); + const auto& evRecord = ev->Get()->Record; + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvProposeTransactionAttachResult" + << " triggers early" + << ", at schemeshard: " << ssId + << " message# " << evRecord.ShortDebugString()); + + const bool collected = CollectPQConfigChanged(OperationId, ev, context); + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " HandleReply TEvProposeTransactionAttachResult" + << " CollectPQConfigChanged: " << (collected ? "true" : "false")); + + return TryPersistState(context); +} + +bool TPropose::HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) +{ + TStepId step = TStepId(ev->Get()->StepId); + TTabletId ssId = context.SS->SelfTabletId(); + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() + << " HandleReply TEvOperationPlan" + << ", step: " << step + << ", at tablet: " << ssId); + + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreatePQGroup || txState->TxType == TTxState::TxAlterPQGroup); + + TPathId pathId = txState->TargetPathId; + TPathElement::TPtr path = context.SS->PathsById.at(pathId); + + NIceDb::TNiceDb db(context.GetDB()); + + if (path->StepCreated == InvalidStepId) { + path->StepCreated = step; + context.SS->PersistCreateStep(db, pathId, step); + } + + return TryPersistState(context); +} + +bool TPropose::ProgressState(TOperationContext& context) +{ + TTabletId ssId = context.SS->SelfTabletId(); + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "NPQState::TPropose ProgressState" + << ", operationId: " << OperationId + << ", at schemeshard: " << ssId); + + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreatePQGroup || txState->TxType == TTxState::TxAlterPQGroup); + + // + // If the program works according to the new scheme, then we must add PQ tablets to the list for + // the Coordinator. At this stage, we cannot rely on the value of + // the EnablePQConfigTransactionsAtSchemeShard flag. Because the operation could have started on one tablet + // and moved to another by that time. + // + // Therefore, here we check the value of the minStep field, which is filled in in + // the TEvProposeTransactionResult handler + // + TSet<TTabletId> shardSet; + if (ui64(txState->MinStep) > 0) { + PrepareShards(*txState, shardSet, context); + } + context.OnComplete.ProposeToCoordinator(OperationId, txState->TargetPathId, txState->MinStep, shardSet); + + return false; +} + +void TPropose::PrepareShards(TTxState& txState, TSet<TTabletId>& shardSet, TOperationContext& context) +{ + txState.UpdateShardsInProgress(); + + for (const auto& shard : txState.Shards) { + const TShardIdx idx = shard.Idx; + // + // The operation involves the ETabletType::PersQueue and Tabletype::PersQueueReadBalancer shards. + // The program receives responses from PersQueueReadBalancer at the previous stage. At this stage, + // it only expects TEvProposeTransactionResult from PersQueue + // + if (shard.TabletType == ETabletType::PersQueue) { + const TTabletId tablet = context.SS->ShardInfos.at(idx).TabletID; + + shardSet.insert(tablet); + + // + // By this point, the SchemeShard tablet could restart and the actor ID changed. Therefore, we send + // the TEvProposeTransactionAttach message to the PQ tablets so that they recognize the new recipient + // + SendEvProposeTransactionAttach(idx, tablet, context); + } else { + txState.ShardsInProgress.erase(idx); + } + } +} + +void TPropose::SendEvProposeTransactionAttach(TShardIdx shard, TTabletId tablet, + TOperationContext& context) +{ + auto event = + MakeHolder<TEvPersQueue::TEvProposeTransactionAttach>(ui64(tablet), + ui64(OperationId.GetTxId())); + context.OnComplete.BindMsgToPipe(OperationId, tablet, shard, event.Release()); +} + +bool TPropose::CanPersistState(const TTxState& txState, + TOperationContext& context) +{ + if (!txState.ShardsInProgress.empty()) { + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " can't persist state: " << + "ShardsInProgress is not empty, remain: " << txState.ShardsInProgress.size()); + return false; + } + + PathId = txState.TargetPathId; + Path = context.SS->PathsById.at(PathId); + + if (Path->StepCreated == InvalidStepId) { + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << " can't persist state: " << + "StepCreated is invalid"); + return false; + } + + return true; +} + +void TPropose::PersistState(const TTxState& txState, + TOperationContext& context) const +{ + NIceDb::TNiceDb db(context.GetDB()); + + if (txState.TxType == TTxState::TxCreatePQGroup) { + auto parentDir = context.SS->PathsById.at(Path->ParentPathId); + ++parentDir->DirAlterVersion; + context.SS->PersistPathDirAlterVersion(db, parentDir); + context.SS->ClearDescribePathCaches(parentDir); + context.OnComplete.PublishToSchemeBoard(OperationId, parentDir->PathId); + } + + context.SS->ClearDescribePathCaches(Path); + context.OnComplete.PublishToSchemeBoard(OperationId, PathId); + + TTopicInfo::TPtr pqGroup = context.SS->Topics[PathId]; + + NKikimrPQ::TPQTabletConfig tabletConfig = pqGroup->GetTabletConfig(); + NKikimrPQ::TPQTabletConfig newTabletConfig = pqGroup->AlterData->GetTabletConfig(); + + pqGroup->FinishAlter(); + + context.SS->PersistPersQueueGroup(db, PathId, pqGroup); + context.SS->PersistRemovePersQueueGroupAlter(db, PathId); + + context.SS->ChangeTxState(db, OperationId, TTxState::Done); +} + +bool TPropose::TryPersistState(TOperationContext& context) +{ + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + + if (!CanPersistState(*txState, context)) { + return false; + } + + PersistState(*txState, context); + + return true; +} + +} // namespace NKikimr::NSchemeShard::NPQState diff --git a/ydb/core/tx/schemeshard/schemeshard__operation_common_subdomain.cpp b/ydb/core/tx/schemeshard/schemeshard__operation_common_subdomain.cpp new file mode 100644 index 00000000000..7166bb69c2b --- /dev/null +++ b/ydb/core/tx/schemeshard/schemeshard__operation_common_subdomain.cpp @@ -0,0 +1,393 @@ +#include "schemeshard__operation_part.h" +#include "schemeshard__operation_common_subdomain.h" + +#include <ydb/core/base/hive.h> +#include "schemeshard_private.h" + +#include <ydb/core/statistics/events.h> + +namespace NKikimr::NSchemeShard::NSubDomainState { + +// NSubDomainState::TConfigureParts +// +TConfigureParts::TConfigureParts(TOperationId id) + : OperationId(id) +{ + IgnoreMessages(DebugHint(), {TEvHive::TEvCreateTabletReply::EventType}); +} + +bool TConfigureParts::HandleReply(TEvSchemeShard::TEvInitTenantSchemeShardResult::TPtr& ev, TOperationContext& context) { + TTabletId ssId = context.SS->SelfTabletId(); + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() + << " HandleReply TEvInitTenantSchemeShardResult" + << " operationId: " << OperationId + << " at schemeshard: " << ssId); + + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateSubDomain + || txState->TxType == TTxState::TxAlterSubDomain + || txState->TxType == TTxState::TxAlterExtSubDomain + || txState->TxType == TTxState::TxAlterExtSubDomainCreateHive + ); + + const auto& record = ev->Get()->Record; + + NIceDb::TNiceDb db(context.GetDB()); + + TTabletId tabletId = TTabletId(record.GetTenantSchemeShard()); + auto status = record.GetStatus(); + + auto shardIdx = context.SS->MustGetShardIdx(tabletId); + Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shardIdx)); + + if (status != NKikimrScheme::EStatus::StatusSuccess && status != NKikimrScheme::EStatus::StatusAlreadyExists) { + LOG_CRIT_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() + << " Got error status on SubDomain Configure" + << "from tenant schemeshard tablet: " << tabletId + << " shard: " << shardIdx + << " status: " << NKikimrScheme::EStatus_Name(status) + << " opId: " << OperationId + << " schemeshard: " << ssId); + return false; + } + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() + << " Got OK TEvInitTenantSchemeShardResult from schemeshard" + << " tablet: " << tabletId + << " shardIdx: " << shardIdx + << " at schemeshard: " << ssId); + + txState->ShardsInProgress.erase(shardIdx); + context.OnComplete.UnbindMsgFromPipe(OperationId, tabletId, shardIdx); + + if (txState->ShardsInProgress.empty()) { + // All tablets have replied so we can done this transaction + context.SS->ChangeTxState(db, OperationId, TTxState::Propose); + context.OnComplete.ActivateTx(OperationId); + return true; + } + + return false; +} + +bool TConfigureParts::HandleReply(TEvSubDomain::TEvConfigureStatus::TPtr& ev, TOperationContext& context) { + TTabletId ssId = context.SS->SelfTabletId(); + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() + << " HandleReply TEvConfigureStatus" + << " operationId:" << OperationId + << " at schemeshard:" << ssId); + + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateSubDomain + || txState->TxType == TTxState::TxAlterSubDomain + || txState->TxType == TTxState::TxAlterExtSubDomain + || txState->TxType == TTxState::TxAlterExtSubDomainCreateHive + ); + + const auto& record = ev->Get()->Record; + + NIceDb::TNiceDb db(context.GetDB()); + + TTabletId tabletId = TTabletId(record.GetOnTabletId()); + auto status = record.GetStatus(); + + auto shardIdx = context.SS->MustGetShardIdx(tabletId); + Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shardIdx)); + + if (status == NKikimrTx::TEvSubDomainConfigurationAck::REJECT) { + LOG_CRIT_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() + << " Got REJECT on SubDomain Configure" + << "from tablet: " << tabletId + << " shard: " << shardIdx + << " opId: " << OperationId + << " schemeshard: " << ssId); + return false; + } + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() << + " Got OK TEvConfigureStatus from " + << " tablet# " << tabletId + << " shardIdx# " << shardIdx + << " at schemeshard# " << ssId); + + txState->ShardsInProgress.erase(shardIdx); + context.OnComplete.UnbindMsgFromPipe(OperationId, tabletId, shardIdx); + + if (txState->ShardsInProgress.empty()) { + // All tablets have replied so we can done this transaction + context.SS->ChangeTxState(db, OperationId, TTxState::Propose); + context.OnComplete.ActivateTx(OperationId); + return true; + } + + return false; +} + + +bool TConfigureParts::ProgressState(TOperationContext& context) { + TTabletId ssId = context.SS->SelfTabletId(); + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + DebugHint() + << " ProgressState" + << ", at schemeshard: " << ssId); + + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateSubDomain + || txState->TxType == TTxState::TxAlterSubDomain + || txState->TxType == TTxState::TxAlterExtSubDomain + || txState->TxType == TTxState::TxAlterExtSubDomainCreateHive + ); + + txState->ClearShardsInProgress(); + + if (txState->Shards.empty()) { + NIceDb::TNiceDb db(context.GetDB()); + context.SS->ChangeTxState(db, OperationId, TTxState::Propose); + context.OnComplete.ActivateTx(OperationId); + return true; + } + + auto pathId = txState->TargetPathId; + Y_ABORT_UNLESS(context.SS->PathsById.contains(pathId)); + TPath path = TPath::Init(pathId, context.SS); + + Y_ABORT_UNLESS(context.SS->SubDomains.contains(pathId)); + auto subDomain = context.SS->SubDomains.at(pathId); + auto alterData = subDomain->GetAlter(); + Y_ABORT_UNLESS(alterData); + alterData->Initialize(context.SS->ShardInfos); + auto processing = alterData->GetProcessingParams(); + auto storagePools = alterData->GetStoragePools(); + auto& schemeLimits = subDomain->GetSchemeLimits(); + + for (auto& shard : txState->Shards) { + if (shard.Operation != TTxState::CreateParts) { + continue; + } + TShardIdx idx = shard.Idx; + Y_ABORT_UNLESS(context.SS->ShardInfos.contains(idx)); + TTabletId tabletID = context.SS->ShardInfos[idx].TabletID; + auto type = context.SS->ShardInfos[idx].TabletType; + + switch (type) { + case ETabletType::Coordinator: + case ETabletType::Mediator: { + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "Send configure request to coordinator/mediator: " << tabletID << + " opId: " << OperationId << + " schemeshard: " << ssId); + shard.Operation = TTxState::ConfigureParts; + auto event = new TEvSubDomain::TEvConfigure(processing); + context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); + break; + } + case ETabletType::Hive: { + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "Send configure request to hive: " << tabletID << + " opId: " << OperationId << + " schemeshard: " << ssId); + shard.Operation = TTxState::ConfigureParts; + auto event = new TEvHive::TEvConfigureHive(TSubDomainKey(pathId.OwnerId, pathId.LocalPathId)); + context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); + break; + } + case ETabletType::SysViewProcessor: { + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "Send configure request to sys view processor: " << tabletID << + " opId: " << OperationId << + " schemeshard: " << ssId); + auto event = new NSysView::TEvSysView::TEvConfigureProcessor(path.PathString()); + shard.Operation = TTxState::ConfigureParts; + context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); + break; + } + case ETabletType::StatisticsAggregator: { + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "Send configure request to statistics aggregator: " << tabletID << + " opId: " << OperationId << + " schemeshard: " << ssId); + auto event = new NStat::TEvStatistics::TEvConfigureAggregator(path.PathString()); + shard.Operation = TTxState::ConfigureParts; + context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); + break; + } + case ETabletType::SchemeShard: { + auto event = new TEvSchemeShard::TEvInitTenantSchemeShard(ui64(ssId), + pathId.LocalPathId, path.PathString(), + path.Base()->Owner, path.GetEffectiveACL(), path.GetEffectiveACLVersion(), + processing, storagePools, + path.Base()->UserAttrs->Attrs, path.Base()->UserAttrs->AlterVersion, + schemeLimits, ui64(alterData->GetSharedHive()), alterData->GetResourcesDomainId() + ); + if (alterData->GetDeclaredSchemeQuotas()) { + event->Record.MutableDeclaredSchemeQuotas()->CopyFrom(*alterData->GetDeclaredSchemeQuotas()); + } + if (alterData->GetDatabaseQuotas()) { + event->Record.MutableDatabaseQuotas()->CopyFrom(*alterData->GetDatabaseQuotas()); + } + if (alterData->GetAuditSettings()) { + event->Record.MutableAuditSettings()->CopyFrom(*alterData->GetAuditSettings()); + } + if (alterData->GetServerlessComputeResourcesMode()) { + event->Record.SetServerlessComputeResourcesMode(*alterData->GetServerlessComputeResourcesMode()); + } + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "Send configure request to schemeshard: " << tabletID << + " opId: " << OperationId << + " schemeshard: " << ssId << + " msg: " << event->Record.ShortDebugString()); + + shard.Operation = TTxState::ConfigureParts; + context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); + break; + } + case ETabletType::GraphShard: { + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "Send configure request to graph shard: " << tabletID << + " opId: " << OperationId << + " schemeshard: " << ssId); + shard.Operation = TTxState::ConfigureParts; + auto event = new TEvSubDomain::TEvConfigure(processing); + context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); + break; + } + case ETabletType::BackupController: { + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "Send configure request to backup controller tablet: " << tabletID << + " opId: " << OperationId << + " schemeshard: " << ssId); + shard.Operation = TTxState::ConfigureParts; + auto event = new TEvSubDomain::TEvConfigure(processing); + context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); + break; + } + default: + Y_FAIL_S("Unexpected type, we don't create tablets with type " << ETabletType::TypeToStr(type)); + } + } + + txState->UpdateShardsInProgress(TTxState::ConfigureParts); + return false; +} + + +// NSubDomainState::TPropose +// +TPropose::TPropose(TOperationId id) + : OperationId(id) +{ + IgnoreMessages(DebugHint(), { + TEvHive::TEvCreateTabletReply::EventType, + TEvSubDomain::TEvConfigureStatus::EventType, + TEvPrivate::TEvCompleteBarrier::EventType, + }); +} + +bool TPropose::HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) { + TStepId step = TStepId(ev->Get()->StepId); + TTabletId ssId = context.SS->SelfTabletId(); + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "NSubDomainState::TPropose HandleReply TEvOperationPlan" + << ", operationId " << OperationId + << ", at tablet " << ssId); + + TTxState* txState = context.SS->FindTx(OperationId); + if (!txState) { + return false; + } + Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateSubDomain + || txState->TxType == TTxState::TxAlterSubDomain + || txState->TxType == TTxState::TxCreateExtSubDomain + || txState->TxType == TTxState::TxAlterExtSubDomain + || txState->TxType == TTxState::TxAlterExtSubDomainCreateHive + ); + + TPathId pathId = txState->TargetPathId; + Y_ABORT_UNLESS(context.SS->PathsById.contains(pathId)); + TPathElement::TPtr path = context.SS->PathsById.at(pathId); + + NIceDb::TNiceDb db(context.GetDB()); + + if (path->StepCreated == InvalidStepId) { + path->StepCreated = step; + context.SS->PersistCreateStep(db, pathId, step); + } + + Y_ABORT_UNLESS(context.SS->SubDomains.contains(pathId)); + auto subDomain = context.SS->SubDomains.at(pathId); + auto alter = subDomain->GetAlter(); + Y_ABORT_UNLESS(alter); + Y_VERIFY_S(subDomain->GetVersion() < alter->GetVersion(), "" << subDomain->GetVersion() << " and " << alter->GetVersion()); + + subDomain->ActualizeAlterData(context.SS->ShardInfos, context.Ctx.Now(), + /* isExternal */ path->PathType == TPathElement::EPathType::EPathTypeExtSubDomain, + context.SS); + + context.SS->SubDomains[pathId] = alter; + context.SS->PersistSubDomain(db, pathId, *alter); + context.SS->PersistSubDomainSchemeQuotas(db, pathId, *alter); + + if (txState->TxType == TTxState::TxCreateSubDomain || txState->TxType == TTxState::TxCreateExtSubDomain) { + auto parentDir = context.SS->PathsById.at(path->ParentPathId); + ++parentDir->DirAlterVersion; + context.SS->PersistPathDirAlterVersion(db, parentDir); + context.SS->ClearDescribePathCaches(parentDir); + context.OnComplete.PublishToSchemeBoard(OperationId, parentDir->PathId); + } + + context.OnComplete.UpdateTenant(pathId); + context.SS->ClearDescribePathCaches(path); + context.OnComplete.PublishToSchemeBoard(OperationId, pathId); + + if (txState->NeedSyncHive) { + context.SS->ChangeTxState(db, OperationId, TTxState::SyncHive); + } else { + context.SS->ChangeTxState(db, OperationId, TTxState::Done); + } + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "NSubDomainState::TPropose HandleReply TEvOperationPlan" + << ", operationId " << OperationId + << ", at tablet " << ssId); + + return true; +} + +bool TPropose::ProgressState(TOperationContext& context) { + TTabletId ssId = context.SS->SelfTabletId(); + + LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "NSubDomainState::TPropose ProgressState" + << ", operationId: " << OperationId + << ", at schemeshard: " << ssId); + + TTxState* txState = context.SS->FindTx(OperationId); + Y_ABORT_UNLESS(txState); + Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateSubDomain + || txState->TxType == TTxState::TxAlterSubDomain + || txState->TxType == TTxState::TxCreateExtSubDomain + || txState->TxType == TTxState::TxAlterExtSubDomain + || txState->TxType == TTxState::TxAlterExtSubDomainCreateHive + ); + + context.OnComplete.ProposeToCoordinator(OperationId, txState->TargetPathId, TStepId(0)); + + LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, + "NSubDomainState::TPropose ProgressState leave" + << ", operationId " << OperationId + << ", at tablet " << ssId); + + return false; +} + +} // namespace NKikimr::NSchemeShard::NSubDomainState diff --git a/ydb/core/tx/schemeshard/schemeshard__operation_common_subdomain.h b/ydb/core/tx/schemeshard/schemeshard__operation_common_subdomain.h index a2d422c954b..6afcde46dca 100644 --- a/ydb/core/tx/schemeshard/schemeshard__operation_common_subdomain.h +++ b/ydb/core/tx/schemeshard/schemeshard__operation_common_subdomain.h @@ -3,8 +3,7 @@ #include "schemeshard_impl.h" #include "schemeshard__operation_part.h" -namespace NKikimr { -namespace NSchemeShard { +namespace NKikimr::NSchemeShard { inline bool CheckStoragePoolsInQuotas( const Ydb::Cms::DatabaseQuotas& quotas, @@ -60,274 +59,11 @@ private: << " operationId#" << OperationId; } public: - TConfigureParts(TOperationId id) - : OperationId(id) - { - IgnoreMessages(DebugHint(), {TEvHive::TEvCreateTabletReply::EventType}); - } - - bool HandleReply(TEvSchemeShard::TEvInitTenantSchemeShardResult::TPtr& ev, TOperationContext& context) override { - TTabletId ssId = context.SS->SelfTabletId(); - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() - << " HandleReply TEvInitTenantSchemeShardResult" - << " operationId: " << OperationId - << " at schemeshard: " << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateSubDomain - || txState->TxType == TTxState::TxAlterSubDomain - || txState->TxType == TTxState::TxAlterExtSubDomain - || txState->TxType == TTxState::TxAlterExtSubDomainCreateHive - ); - - const auto& record = ev->Get()->Record; - - NIceDb::TNiceDb db(context.GetDB()); - - TTabletId tabletId = TTabletId(record.GetTenantSchemeShard()); - auto status = record.GetStatus(); - - auto shardIdx = context.SS->MustGetShardIdx(tabletId); - Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shardIdx)); - - if (status != NKikimrScheme::EStatus::StatusSuccess && status != NKikimrScheme::EStatus::StatusAlreadyExists) { - LOG_CRIT_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() - << " Got error status on SubDomain Configure" - << "from tenant schemeshard tablet: " << tabletId - << " shard: " << shardIdx - << " status: " << NKikimrScheme::EStatus_Name(status) - << " opId: " << OperationId - << " schemeshard: " << ssId); - return false; - } - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() - << " Got OK TEvInitTenantSchemeShardResult from schemeshard" - << " tablet: " << tabletId - << " shardIdx: " << shardIdx - << " at schemeshard: " << ssId); - - txState->ShardsInProgress.erase(shardIdx); - context.OnComplete.UnbindMsgFromPipe(OperationId, tabletId, shardIdx); - - if (txState->ShardsInProgress.empty()) { - // All tablets have replied so we can done this transaction - context.SS->ChangeTxState(db, OperationId, TTxState::Propose); - context.OnComplete.ActivateTx(OperationId); - return true; - } - - return false; - } - - bool HandleReply(TEvSubDomain::TEvConfigureStatus::TPtr& ev, TOperationContext& context) override { - TTabletId ssId = context.SS->SelfTabletId(); - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() - << " HandleReply TEvConfigureStatus" - << " operationId:" << OperationId - << " at schemeshard:" << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateSubDomain - || txState->TxType == TTxState::TxAlterSubDomain - || txState->TxType == TTxState::TxAlterExtSubDomain - || txState->TxType == TTxState::TxAlterExtSubDomainCreateHive - ); - - const auto& record = ev->Get()->Record; - - NIceDb::TNiceDb db(context.GetDB()); - - TTabletId tabletId = TTabletId(record.GetOnTabletId()); - auto status = record.GetStatus(); - - auto shardIdx = context.SS->MustGetShardIdx(tabletId); - Y_ABORT_UNLESS(context.SS->ShardInfos.contains(shardIdx)); - - if (status == NKikimrTx::TEvSubDomainConfigurationAck::REJECT) { - LOG_CRIT_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() - << " Got REJECT on SubDomain Configure" - << "from tablet: " << tabletId - << " shard: " << shardIdx - << " opId: " << OperationId - << " schemeshard: " << ssId); - return false; - } - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() << - " Got OK TEvConfigureStatus from " - << " tablet# " << tabletId - << " shardIdx# " << shardIdx - << " at schemeshard# " << ssId); - - txState->ShardsInProgress.erase(shardIdx); - context.OnComplete.UnbindMsgFromPipe(OperationId, tabletId, shardIdx); - - if (txState->ShardsInProgress.empty()) { - // All tablets have replied so we can done this transaction - context.SS->ChangeTxState(db, OperationId, TTxState::Propose); - context.OnComplete.ActivateTx(OperationId); - return true; - } - - return false; - } - - - bool ProgressState(TOperationContext& context) override { - TTabletId ssId = context.SS->SelfTabletId(); - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - DebugHint() - << " ProgressState" - << ", at schemeshard: " << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateSubDomain - || txState->TxType == TTxState::TxAlterSubDomain - || txState->TxType == TTxState::TxAlterExtSubDomain - || txState->TxType == TTxState::TxAlterExtSubDomainCreateHive - ); - - txState->ClearShardsInProgress(); + TConfigureParts(TOperationId id); - if (txState->Shards.empty()) { - NIceDb::TNiceDb db(context.GetDB()); - context.SS->ChangeTxState(db, OperationId, TTxState::Propose); - context.OnComplete.ActivateTx(OperationId); - return true; - } - - auto pathId = txState->TargetPathId; - Y_ABORT_UNLESS(context.SS->PathsById.contains(pathId)); - TPath path = TPath::Init(pathId, context.SS); - - Y_ABORT_UNLESS(context.SS->SubDomains.contains(pathId)); - auto subDomain = context.SS->SubDomains.at(pathId); - auto alterData = subDomain->GetAlter(); - Y_ABORT_UNLESS(alterData); - alterData->Initialize(context.SS->ShardInfos); - auto processing = alterData->GetProcessingParams(); - auto storagePools = alterData->GetStoragePools(); - auto& schemeLimits = subDomain->GetSchemeLimits(); - - for (auto& shard : txState->Shards) { - if (shard.Operation != TTxState::CreateParts) { - continue; - } - TShardIdx idx = shard.Idx; - Y_ABORT_UNLESS(context.SS->ShardInfos.contains(idx)); - TTabletId tabletID = context.SS->ShardInfos[idx].TabletID; - auto type = context.SS->ShardInfos[idx].TabletType; - - switch (type) { - case ETabletType::Coordinator: - case ETabletType::Mediator: { - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "Send configure request to coordinator/mediator: " << tabletID << - " opId: " << OperationId << - " schemeshard: " << ssId); - shard.Operation = TTxState::ConfigureParts; - auto event = new TEvSubDomain::TEvConfigure(processing); - context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); - break; - } - case ETabletType::Hive: { - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "Send configure request to hive: " << tabletID << - " opId: " << OperationId << - " schemeshard: " << ssId); - shard.Operation = TTxState::ConfigureParts; - auto event = new TEvHive::TEvConfigureHive(TSubDomainKey(pathId.OwnerId, pathId.LocalPathId)); - context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); - break; - } - case ETabletType::SysViewProcessor: { - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "Send configure request to sys view processor: " << tabletID << - " opId: " << OperationId << - " schemeshard: " << ssId); - auto event = new NSysView::TEvSysView::TEvConfigureProcessor(path.PathString()); - shard.Operation = TTxState::ConfigureParts; - context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); - break; - } - case ETabletType::StatisticsAggregator: { - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "Send configure request to statistics aggregator: " << tabletID << - " opId: " << OperationId << - " schemeshard: " << ssId); - auto event = new NStat::TEvStatistics::TEvConfigureAggregator(path.PathString()); - shard.Operation = TTxState::ConfigureParts; - context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); - break; - } - case ETabletType::SchemeShard: { - auto event = new TEvSchemeShard::TEvInitTenantSchemeShard(ui64(ssId), - pathId.LocalPathId, path.PathString(), - path.Base()->Owner, path.GetEffectiveACL(), path.GetEffectiveACLVersion(), - processing, storagePools, - path.Base()->UserAttrs->Attrs, path.Base()->UserAttrs->AlterVersion, - schemeLimits, ui64(alterData->GetSharedHive()), alterData->GetResourcesDomainId() - ); - if (alterData->GetDeclaredSchemeQuotas()) { - event->Record.MutableDeclaredSchemeQuotas()->CopyFrom(*alterData->GetDeclaredSchemeQuotas()); - } - if (alterData->GetDatabaseQuotas()) { - event->Record.MutableDatabaseQuotas()->CopyFrom(*alterData->GetDatabaseQuotas()); - } - if (alterData->GetAuditSettings()) { - event->Record.MutableAuditSettings()->CopyFrom(*alterData->GetAuditSettings()); - } - if (alterData->GetServerlessComputeResourcesMode()) { - event->Record.SetServerlessComputeResourcesMode(*alterData->GetServerlessComputeResourcesMode()); - } - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "Send configure request to schemeshard: " << tabletID << - " opId: " << OperationId << - " schemeshard: " << ssId << - " msg: " << event->Record.ShortDebugString()); - - shard.Operation = TTxState::ConfigureParts; - context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); - break; - } - case ETabletType::GraphShard: { - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "Send configure request to graph shard: " << tabletID << - " opId: " << OperationId << - " schemeshard: " << ssId); - shard.Operation = TTxState::ConfigureParts; - auto event = new TEvSubDomain::TEvConfigure(processing); - context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); - break; - } - case ETabletType::BackupController: { - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "Send configure request to backup controller tablet: " << tabletID << - " opId: " << OperationId << - " schemeshard: " << ssId); - shard.Operation = TTxState::ConfigureParts; - auto event = new TEvSubDomain::TEvConfigure(processing); - context.OnComplete.BindMsgToPipe(OperationId, tabletID, idx, event); - break; - } - default: - Y_FAIL_S("Unexpected type, we don't create tablets with type " << ETabletType::TypeToStr(type)); - } - } - - txState->UpdateShardsInProgress(TTxState::ConfigureParts); - return false; - } + bool ProgressState(TOperationContext& context) override; + bool HandleReply(TEvSchemeShard::TEvInitTenantSchemeShardResult::TPtr& ev, TOperationContext& context) override; + bool HandleReply(TEvSubDomain::TEvConfigureStatus::TPtr& ev, TOperationContext& context) override; }; class TPropose: public TSubOperationState { @@ -341,116 +77,12 @@ private: } public: - TPropose(TOperationId id) - : OperationId(id) - { - IgnoreMessages(DebugHint(), { - TEvHive::TEvCreateTabletReply::EventType, - TEvSubDomain::TEvConfigureStatus::EventType, - TEvPrivate::TEvCompleteBarrier::EventType, - }); - } - - bool HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) override { - TStepId step = TStepId(ev->Get()->StepId); - TTabletId ssId = context.SS->SelfTabletId(); - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "NSubDomainState::TPropose HandleReply TEvOperationPlan" - << ", operationId " << OperationId - << ", at tablet " << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - if (!txState) { - return false; - } - Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateSubDomain - || txState->TxType == TTxState::TxAlterSubDomain - || txState->TxType == TTxState::TxCreateExtSubDomain - || txState->TxType == TTxState::TxAlterExtSubDomain - || txState->TxType == TTxState::TxAlterExtSubDomainCreateHive - ); - - TPathId pathId = txState->TargetPathId; - Y_ABORT_UNLESS(context.SS->PathsById.contains(pathId)); - TPathElement::TPtr path = context.SS->PathsById.at(pathId); - - NIceDb::TNiceDb db(context.GetDB()); - - if (path->StepCreated == InvalidStepId) { - path->StepCreated = step; - context.SS->PersistCreateStep(db, pathId, step); - } + TPropose(TOperationId id); - Y_ABORT_UNLESS(context.SS->SubDomains.contains(pathId)); - auto subDomain = context.SS->SubDomains.at(pathId); - auto alter = subDomain->GetAlter(); - Y_ABORT_UNLESS(alter); - Y_VERIFY_S(subDomain->GetVersion() < alter->GetVersion(), "" << subDomain->GetVersion() << " and " << alter->GetVersion()); - - subDomain->ActualizeAlterData(context.SS->ShardInfos, context.Ctx.Now(), - /* isExternal */ path->PathType == TPathElement::EPathType::EPathTypeExtSubDomain, - context.SS); - - context.SS->SubDomains[pathId] = alter; - context.SS->PersistSubDomain(db, pathId, *alter); - context.SS->PersistSubDomainSchemeQuotas(db, pathId, *alter); - - if (txState->TxType == TTxState::TxCreateSubDomain || txState->TxType == TTxState::TxCreateExtSubDomain) { - auto parentDir = context.SS->PathsById.at(path->ParentPathId); - ++parentDir->DirAlterVersion; - context.SS->PersistPathDirAlterVersion(db, parentDir); - context.SS->ClearDescribePathCaches(parentDir); - context.OnComplete.PublishToSchemeBoard(OperationId, parentDir->PathId); - } - - context.OnComplete.UpdateTenant(pathId); - context.SS->ClearDescribePathCaches(path); - context.OnComplete.PublishToSchemeBoard(OperationId, pathId); - - if (txState->NeedSyncHive) { - context.SS->ChangeTxState(db, OperationId, TTxState::SyncHive); - } else { - context.SS->ChangeTxState(db, OperationId, TTxState::Done); - } - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "NSubDomainState::TPropose HandleReply TEvOperationPlan" - << ", operationId " << OperationId - << ", at tablet " << ssId); - - return true; - } - - bool ProgressState(TOperationContext& context) override { - TTabletId ssId = context.SS->SelfTabletId(); - - LOG_INFO_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "NSubDomainState::TPropose ProgressState" - << ", operationId: " << OperationId - << ", at schemeshard: " << ssId); - - TTxState* txState = context.SS->FindTx(OperationId); - Y_ABORT_UNLESS(txState); - Y_ABORT_UNLESS(txState->TxType == TTxState::TxCreateSubDomain - || txState->TxType == TTxState::TxAlterSubDomain - || txState->TxType == TTxState::TxCreateExtSubDomain - || txState->TxType == TTxState::TxAlterExtSubDomain - || txState->TxType == TTxState::TxAlterExtSubDomainCreateHive - ); - - context.OnComplete.ProposeToCoordinator(OperationId, txState->TargetPathId, TStepId(0)); - - LOG_DEBUG_S(context.Ctx, NKikimrServices::FLAT_TX_SCHEMESHARD, - "NSubDomainState::TPropose ProgressState leave" - << ", operationId " << OperationId - << ", at tablet " << ssId); - - return false; - } + bool ProgressState(TOperationContext& context) override; + bool HandleReply(TEvPrivate::TEvOperationPlan::TPtr& ev, TOperationContext& context) override; }; } // namespace NSubDomainState -} // namespace NSchemeShard -} // namespace NKikimr +} // namespace NKikimr::NSchemeShard diff --git a/ydb/core/tx/schemeshard/ya.make b/ydb/core/tx/schemeshard/ya.make index 7e0a802b9f2..7c71e695124 100644 --- a/ydb/core/tx/schemeshard/ya.make +++ b/ydb/core/tx/schemeshard/ya.make @@ -103,12 +103,16 @@ SRCS( schemeshard__operation_blob_depot.cpp schemeshard__operation_cancel_tx.cpp schemeshard__operation_cansel_build_index.cpp - schemeshard__operation_common.cpp schemeshard__operation_common.h + schemeshard__operation_common.cpp + schemeshard__operation_common_pq.cpp + schemeshard__operation_common_bsv.cpp + schemeshard__operation_common_cdc_stream.cpp schemeshard__operation_common_external_data_source.cpp schemeshard__operation_common_external_table.cpp schemeshard__operation_common_resource_pool.cpp schemeshard__operation_common_subdomain.h + schemeshard__operation_common_subdomain.cpp schemeshard__operation_consistent_copy_tables.cpp schemeshard__operation_copy_sequence.cpp schemeshard__operation_copy_table.cpp |
