diff --git a/ydb/apps/dstool/lib/dstool_cmd_group_decommit.py b/ydb/apps/dstool/lib/dstool_cmd_group_decommit.py index 57f558f9881f..7bc41d396ee4 100644 --- a/ydb/apps/dstool/lib/dstool_cmd_group_decommit.py +++ b/ydb/apps/dstool/lib/dstool_cmd_group_decommit.py @@ -1,6 +1,7 @@ import ydb.apps.dstool.lib.common as common import ydb.core.protos.blob_depot_config_pb2 as blob_depot_config import sys +import time description = 'Decommit physical group' @@ -8,21 +9,18 @@ def add_options(p): common.add_group_ids_option(p, required=True) g = p.add_mutually_exclusive_group(required=True) - g.add_argument('--hive-id', type=int, help='tablet id of containing hive') - g.add_argument('--database', type=str, help='database path of containing hive') + g.add_argument('--database', type=str, required=True, help='database path for storage pools with groups being decommitted') p.add_argument('--log-channel-sp', type=str, metavar='POOL_NAME', help='channel 0 specifier') p.add_argument('--snapshot-channel-sp', type=str, metavar='POOL_NAME', help='channel 1 specifier (defaults to channel 0)') p.add_argument('--data-channel-sp', type=str, metavar='POOL_NAME[*COUNT]', nargs='*', help='data channel specifier') + p.add_argument('--wait', action='store_true', help='wait until group decommission is started') def do(args): request = common.create_bsc_request(args) cmd = request.Command.add().DecommitGroups cmd.GroupIds.extend(args.group_ids) - if args.hive_id is not None: - cmd.HiveId = args.hive_id - if args.database is not None: - cmd.Database = args.database + cmd.Database = args.database if args.log_channel_sp or args.snapshot_channel_sp or args.data_channel_sp: if args.log_channel_sp is None: @@ -44,4 +42,39 @@ def do(args): cmd.ChannelProfiles.add(StoragePoolName=pool_name, ChannelKind=blob_depot_config.TChannelKind.Data, Count=count) response = common.invoke_bsc_request(request) - common.print_request_result(args, request, response) + + if args.wait and response.Success: + groups_of_interest = set(args.group_ids) + + while groups_of_interest: + time.sleep(1) + base_config_and_storage_pools = common.fetch_base_config_and_storage_pools(virtualGroupsOnly=True) + base_config = base_config_and_storage_pools['BaseConfig'] + group_map = common.build_group_map(base_config) + for group_id in list(groups_of_interest): + if group_id not in group_map: + print(f'Group {group_id} is missing in group list', file=sys.stderr) + groups_of_interest.remove(group_id) + continue + group = group_map[group_id] + if group.VirtualGroupInfo is None: + print(f'Group {group_id} is not virtual group', file=sys.stderr) + elif group.VirtualGroupInfo.State == common.EVirtualGroupState.WORKING: + pass + elif group.VirtualGroupInfo.State == common.EVirtualGroupState.CREATE_FAILED: + print(f'Group {group_id} decommission failed to start: {group.VirtualGroupInfo.ErrorReason}, canceling', file=sys.stderr) + request = common.kikimr_bsconfig.TConfigRequest() + cmd = request.Command.add().CancelVirtualGroup + cmd.GroupId = group_id + response = common.invoke_bsc_request(request) + if not response.Success: + print(f'Failed to cancel decommission for group {group_id}', file=sys.stderr) + elif group.VirtualGroupInfo.State == common.EVirtualGroupState.DELETING: + print(f'Group {group_id} is being deleted', file=sys.stderr) + elif group.VirtualGroupInfo.State == common.EVirtualGroupState.NEW: + continue + else: + print(f'Group {group_id} has unexpected virtual group state', file=sys.stderr) + groups_of_interest.remove(group_id) + else: + common.print_request_result(args, request, response) diff --git a/ydb/core/blobstorage/ut_blobstorage/blob_depot_test_functions.cpp b/ydb/core/blobstorage/ut_blobstorage/blob_depot_test_functions.cpp index 77444585df3b..0d78930ec8e6 100644 --- a/ydb/core/blobstorage/ut_blobstorage/blob_depot_test_functions.cpp +++ b/ydb/core/blobstorage/ut_blobstorage/blob_depot_test_functions.cpp @@ -15,7 +15,7 @@ void DecommitGroup(TBlobDepotTestEnvironment& tenv, ui32 groupId) { auto *cmd = request.AddCommand()->MutableDecommitGroups(); cmd->AddGroupIds(groupId); - cmd->SetHiveId(tenv.Env->Runtime->GetDomainsInfo()->GetHive()); + cmd->SetDatabase(TStringBuilder() << '/' << tenv.Env->Runtime->GetDomainsInfo()->GetDomain()->Name); auto *prof = cmd->AddChannelProfiles(); prof->SetStoragePoolName(blobDepotPool); prof->SetCount(2); diff --git a/ydb/core/mind/bscontroller/virtual_group.cpp b/ydb/core/mind/bscontroller/virtual_group.cpp index f220375087b1..7f27350eb2e6 100644 --- a/ydb/core/mind/bscontroller/virtual_group.cpp +++ b/ydb/core/mind/bscontroller/virtual_group.cpp @@ -107,8 +107,8 @@ namespace NKikimr::NBsController { } void TBlobStorageController::TConfigState::ExecuteStep(const NKikimrBlobStorage::TDecommitGroups& cmd, TStatus& /*status*/) { - if (cmd.GetHiveDesignatorCase() == NKikimrBlobStorage::TDecommitGroups::HIVEDESIGNATOR_NOT_SET) { - throw TExError() << "TDecommitGroups.HiveId/Database is not specified"; + if (!cmd.HasDatabase()) { + throw TExError() << "TDecommitGroups.Database is not specified, but it is mandatory"; } for (const ui32 groupId : cmd.GetGroupIds()) { @@ -116,12 +116,8 @@ namespace NKikimr::NBsController { if (!group) { throw TExGroupNotFound(groupId); } else if (group->DecommitStatus != NKikimrBlobStorage::TGroupDecommitStatus::NONE) { - if (cmd.HasHiveId() && group->HiveId && *group->HiveId != cmd.GetHiveId()) { - throw TExError() << "different hive specified for decommitting group" << TErrorParams::GroupId(groupId); - } else if (cmd.HasDatabase() && group->Database && *group->Database != cmd.GetDatabase()) { + if (group->Database != cmd.GetDatabase()) { throw TExError() << "different database specified for decommitting group" << TErrorParams::GroupId(groupId); - } else if (cmd.HasHiveId() != group->HiveId.Defined() && cmd.HasDatabase() != group->Database.Defined()) { - throw TExError() << "different hive designator specified for decommitting group" << TErrorParams::GroupId(groupId); } // group is already being decommitted -- make this operation idempotent continue; @@ -131,8 +127,7 @@ namespace NKikimr::NBsController { group->DecommitStatus = NKikimrBlobStorage::TGroupDecommitStatus::PENDING; group->VirtualGroupState = NKikimrBlobStorage::EVirtualGroupState::NEW; - group->HiveId = cmd.HasHiveId() ? MakeMaybe(cmd.GetHiveId()) : Nothing(); - group->Database = cmd.HasDatabase() ? MakeMaybe(cmd.GetDatabase()) : Nothing(); + group->Database = cmd.GetDatabase(); group->NeedAlter = true; GroupFailureModelChanged.insert(group->ID); group->CalculateGroupStatus(); @@ -428,16 +423,19 @@ namespace NKikimr::NBsController { ui64 RootHiveId = 0; bool TenantHiveInvalidated = false; bool TenantHiveInvalidateInProgress = false; + bool IsDecommittingGroup = false; void HiveCreate(TGroupInfo *group) { auto& config = GetConfig(group); + IsDecommittingGroup = config.GetIsDecommittingGroup(); + if (config.HasTabletId()) { ConfigureBlobDepot(); } else if (!group->HiveId) { HiveResolve(group); } else if (TenantHiveInvalidateInProgress && TenantHivePipeId) { // tenant hive storage pool invalidation still in progress, wait - } else if (config.GetIsDecommittingGroup() && config.HasTenantHiveId() && !TenantHiveInvalidated) { + } else if (IsDecommittingGroup && config.HasTenantHiveId() && !TenantHiveInvalidated) { TenantHivePipeId = Register(NTabletPipe::CreateClient(SelfId(), config.GetTenantHiveId(), NTabletPipe::TClientRetryPolicy::WithRetries())); HiveInvalidateGroups(TenantHivePipeId, group); @@ -445,7 +443,7 @@ namespace NKikimr::NBsController { } else if (!HivePipeId) { Y_ABORT_UNLESS(group->HiveId); HivePipeId = Register(NTabletPipe::CreateClient(SelfId(), *group->HiveId, NTabletPipe::TClientRetryPolicy::WithRetries())); - if (config.GetIsDecommittingGroup()) { + if (IsDecommittingGroup) { HiveInvalidateGroups(HivePipeId, group); } } else { @@ -487,7 +485,10 @@ namespace NKikimr::NBsController { (Result, response.ToString(*AppData()->TypeRegistry))); if (item.Status != NSchemeCache::TSchemeCacheNavigate::EStatus::Ok || !domainInfo || !item.DomainDescription) { - return CreateFailed(TStringBuilder() << "failed to resolve Hive -- erroneous reply from SchemeCache or not a domain"); + return CreateFailed(TStringBuilder() << "failed to resolve Hive -- erroneous reply from SchemeCache or not a domain" + << " Response# " << response.ToString(*AppData()->TypeRegistry) + << " DomainInfo# " << static_cast(domainInfo) + << " DomainDescription# " << static_cast(item.DomainDescription)); } else if (const auto& params = domainInfo->Params; !params.HasHive() && !RootHiveId) { return CreateFailed("failed to resolve Hive -- no Hive in SchemeCache reply"); } else { @@ -517,7 +518,7 @@ namespace NKikimr::NBsController { } } - const ui64 hiveId = params.HasHive() ? params.GetHive() : RootHiveId; + const ui64 hiveId = params.HasHive() && !IsDecommittingGroup ? params.GetHive() : RootHiveId; if (!hiveId) { return CreateFailed("failed to resolve Hive -- Hive is zero"); } diff --git a/ydb/docs/ru/core/maintenance/manual/blobdepot_decommit.md b/ydb/docs/ru/core/maintenance/manual/blobdepot_decommit.md index 4861c7384d34..797d115d0295 100644 --- a/ydb/docs/ru/core/maintenance/manual/blobdepot_decommit.md +++ b/ydb/docs/ru/core/maintenance/manual/blobdepot_decommit.md @@ -24,14 +24,14 @@ Для запуска декомиссии выполняется команда BS\_CONTROLLER, в которой нужно указать список декомиссуемых групп, а также номер таблетки Hive, которая будет управлять блобовницами декомиссуемых групп. Также можно указать список пулов, в которых блобовница будет хранить свои данные. Если этот список не указан, то BS\_CONTROLLER автоматически выбирает для хранения данных те же пулы, в которых располагаются декомиссуемые группы, а число каналов данных делается равным числу физических групп в этих пулах (но не более 250 штук). ```bash -dstool -e ... --direct group decommit --group-ids 2181038080 --database=/Root/db1 --hive-id=72057594037968897 +dstool -e ... --direct group decommit --group-ids 2181038080 --database=/Root/db1 --wait ``` Параметры командной строки: +* --wait дождаться запуска декомиссии; в случае возникновения ошибки запуска ошибка выводится на экран, декомиссия отменяется автоматически (только при указании этой опции); * --group-ids GROUP\_ID GROUP\_ID список групп, для которых можно провести декомиссию; -* --database=DB указать тенант, в котором нужно делать декомиссию; -* --hive-id=N явно указать номер таблетки Hive, которая будет управлять данной блобовницей; нельзя указывать идентификатор Hive того тенанта, которому принадлежат пулы с декомиссуемыми группами, т.к. этот Hive может хранить свои данные поверх группы, которая управляется блобовницей, что приведёт к циклической зависимости; рекомендуется указывать корневой Hive; +* --database=DB указать тенант, в котором нужно делать декомиссию (или домен, если декомиссия делается для групп внутри домена); * --log-channel-sp=POOL\_NAME название пула, в котором будет размещён канал 0 таблетки блобовницы; * --snapshot-channel-sp=POOL\_NAME название пула, в котором будет размещён кана 1 таблетки блобовницы; если не указан, то используется значение из --log-channel-sp; * --data-channel-sp=POOL\_NAME[\*COUNT] название пула, в котором размещаются каналы данных; если указан параметр COUNT (после знака "звёздочка"), то создаётся COUNT каналов данных в указанном пуле.