diff options
author | Ilia Shakhov <pixcc@ydb.tech> | 2024-01-30 15:53:55 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-30 15:53:55 +0300 |
commit | ae12905f2832ace12b9e9e480bd362d11f785b4d (patch) | |
tree | 37f478def50bf06ecde95f3cdb1ff4114aef0ff6 | |
parent | 59147eb6ee5e83c58e051f84a7599cb60b83ccf4 (diff) | |
download | ydb-ae12905f2832ace12b9e9e480bd362d11f785b4d.tar.gz |
Health check for exclusive dynamic nodes KIKIMR-20818 (#1223)
* Health check for exclusive dynamic nodes KIKIMR-20818
-rw-r--r-- | ydb/core/health_check/health_check.cpp | 51 | ||||
-rw-r--r-- | ydb/core/health_check/health_check_ut.cpp | 881 | ||||
-rw-r--r-- | ydb/public/api/protos/out/out.cpp | 9 |
3 files changed, 919 insertions, 22 deletions
diff --git a/ydb/core/health_check/health_check.cpp b/ydb/core/health_check/health_check.cpp index b34f3daea0..3b3de7c61a 100644 --- a/ydb/core/health_check/health_check.cpp +++ b/ydb/core/health_check/health_check.cpp @@ -228,12 +228,13 @@ public: TTabletId HiveId = {}; TPathId ResourcePathId = {}; TVector<TNodeId> ComputeNodeIds; - TVector<TString> StoragePoolNames; + THashSet<TString> StoragePoolNames; THashMap<std::pair<TTabletId, NNodeWhiteboard::TFollowerId>, const NKikimrHive::TTabletInfo*> MergedTabletState; THashMap<TNodeId, TNodeTabletState> MergedNodeTabletState; THashMap<TNodeId, ui32> NodeRestartsPerPeriod; ui64 StorageQuota; ui64 StorageUsage; + TMaybeServerlessComputeResourcesMode ServerlessComputeResourcesMode; }; struct TSelfCheckResult { @@ -514,7 +515,7 @@ public: TDuration Timeout = TDuration::MilliSeconds(20000); static constexpr TStringBuf STATIC_STORAGE_POOL_NAME = "static"; - bool IsSpecificDatabaseFilter() { + bool IsSpecificDatabaseFilter() const { return FilterDatabase && FilterDatabase != DomainPath; } @@ -593,7 +594,7 @@ public: StoragePoolState[storagePoolName].Groups.emplace(group.groupid()); if (!IsSpecificDatabaseFilter()) { - DatabaseState[DomainPath].StoragePoolNames.emplace_back(storagePoolName); + DatabaseState[DomainPath].StoragePoolNames.emplace(storagePoolName); } } } @@ -869,12 +870,12 @@ public: TDatabaseState& state(DatabaseState[path]); for (const auto& storagePool : ev->Get()->GetRecord().pathdescription().domaindescription().storagepools()) { TString storagePoolName = storagePool.name(); - state.StoragePoolNames.emplace_back(storagePoolName); + state.StoragePoolNames.emplace(storagePoolName); StoragePoolState[storagePoolName].Kind = storagePool.kind(); RequestSelectGroups(storagePoolName); } if (path == DomainPath) { - state.StoragePoolNames.emplace_back(STATIC_STORAGE_POOL_NAME); + state.StoragePoolNames.emplace(STATIC_STORAGE_POOL_NAME); } state.StorageUsage = ev->Get()->GetRecord().pathdescription().domaindescription().diskspaceusage().tables().totalsize(); state.StorageQuota = ev->Get()->GetRecord().pathdescription().domaindescription().databasequotas().data_size_hard_quota(); @@ -888,12 +889,19 @@ public: if (ev->Get()->Request->ResultSet.size() == 1 && ev->Get()->Request->ResultSet.begin()->Status == NSchemeCache::TSchemeCacheNavigate::EStatus::Ok) { auto domainInfo = ev->Get()->Request->ResultSet.begin()->DomainInfo; TString path = CanonizePath(ev->Get()->Request->ResultSet.begin()->Path); - - if (domainInfo->DomainKey != domainInfo->ResourcesDomainKey) { - if (SharedDatabases.emplace(domainInfo->ResourcesDomainKey, path).second) { - RequestSchemeCacheNavigate(domainInfo->ResourcesDomainKey); + if (domainInfo->IsServerless()) { + if (NeedHealthCheckForServerless(domainInfo)) { + if (SharedDatabases.emplace(domainInfo->ResourcesDomainKey, path).second) { + RequestSchemeCacheNavigate(domainInfo->ResourcesDomainKey); + } + DatabaseState[path].ResourcePathId = domainInfo->ResourcesDomainKey; + DatabaseState[path].ServerlessComputeResourcesMode = domainInfo->ServerlessComputeResourcesMode; + } else { + DatabaseState.erase(path); + DatabaseStatusByPath.erase(path); + RequestDone("TEvNavigateKeySetResult"); + return; } - DatabaseState[path].ResourcePathId = domainInfo->ResourcesDomainKey; } TTabletId hiveId = domainInfo->Params.GetHive(); if (hiveId) { @@ -918,6 +926,11 @@ public: RequestDone("TEvNavigateKeySetResult"); } + bool NeedHealthCheckForServerless(TIntrusivePtr<NSchemeCache::TDomainInfo> domainInfo) const { + return IsSpecificDatabaseFilter() + || domainInfo->ServerlessComputeResourcesMode == NKikimrSubDomains::EServerlessComputeResourcesModeExclusive; + } + void Handle(TEvHive::TEvResponseHiveDomainStats::TPtr& ev) { TTabletId hiveId = TabletRequests.CompleteRequest(ev->Cookie); for (const NKikimrHive::THiveDomainStats& hiveStat : ev->Get()->Record.GetDomainStats()) { @@ -951,15 +964,9 @@ public: Ydb::Cms::GetDatabaseStatusResult getTenantStatusResult; operation.result().UnpackTo(&getTenantStatusResult); TString path = getTenantStatusResult.path(); - - bool ignoreServerlessDatabases = !IsSpecificDatabaseFilter(); // we don't ignore sl database if it was exactly specified - if (getTenantStatusResult.has_serverless_resources() && ignoreServerlessDatabases) { - DatabaseState.erase(path); - } else { - DatabaseStatusByPath[path] = std::move(getTenantStatusResult); - DatabaseState[path]; - RequestSchemeCacheNavigate(path); - } + DatabaseStatusByPath[path] = std::move(getTenantStatusResult); + DatabaseState[path]; + RequestSchemeCacheNavigate(path); } RequestDone("TEvGetTenantStatusResponse"); } @@ -1298,7 +1305,9 @@ public: void FillCompute(TDatabaseState& databaseState, Ydb::Monitoring::ComputeStatus& computeStatus, TSelfCheckContext context) { TVector<TNodeId>* computeNodeIds = &databaseState.ComputeNodeIds; - if (databaseState.ResourcePathId) { + if (databaseState.ResourcePathId + && databaseState.ServerlessComputeResourcesMode != NKikimrSubDomains::EServerlessComputeResourcesModeExclusive) + { auto itDatabase = FilterDomainKey.find(TSubDomainKey(databaseState.ResourcePathId.OwnerId, databaseState.ResourcePathId.LocalPathId)); if (itDatabase != FilterDomainKey.end()) { const TString& sharedDatabaseName = itDatabase->second; @@ -2124,7 +2133,7 @@ public: TDatabaseState unknownDatabase; for (auto& [name, pool] : StoragePoolState) { if (StoragePoolSeen.count(name) == 0) { - unknownDatabase.StoragePoolNames.push_back(name); + unknownDatabase.StoragePoolNames.insert(name); } } if (!unknownDatabase.StoragePoolNames.empty()) { diff --git a/ydb/core/health_check/health_check_ut.cpp b/ydb/core/health_check/health_check_ut.cpp index 8cb0857d59..1f199cb2b8 100644 --- a/ydb/core/health_check/health_check_ut.cpp +++ b/ydb/core/health_check/health_check_ut.cpp @@ -8,10 +8,20 @@ #include <ydb/core/tx/schemeshard/schemeshard.h> #include "health_check.cpp" +#include <util/stream/null.h> + namespace NKikimr { using namespace NSchemeShard; using namespace Tests; +using namespace NSchemeCache; +using namespace NNodeWhiteboard; + +#ifdef NDEBUG +#define Ctest Cnull +#else +#define Ctest Cerr +#endif Y_UNIT_TEST_SUITE(THealthCheckTest) { void BasicTest(IEventBase* ev) { @@ -165,7 +175,53 @@ Y_UNIT_TEST_SUITE(THealthCheckTest) { } }; - void AddVSlotInVDiskStateResponse(NNodeWhiteboard::TEvWhiteboard::TEvVDiskStateResponse::TPtr* ev, int groupCount, int vslotCount) { + void AddGroupVSlotInControllerConfigResponseWithStaticGroup(TEvBlobStorage::TEvControllerConfigResponse::TPtr* ev, + const NKikimrBlobStorage::TGroupStatus::E groupStatus, const TVector<NKikimrBlobStorage::EVDiskStatus>& vdiskStatuses) + { + auto& pbRecord = (*ev)->Get()->Record; + auto pbConfig = pbRecord.mutable_response()->mutable_status(0)->mutable_baseconfig(); + + auto groupSample = pbConfig->group(0); + auto vslotSample = pbConfig->vslot(0); + auto vslotIdSample = pbConfig->group(0).vslotid(0); + for (auto& group: *pbConfig->mutable_group()) { + group.set_operatingstatus(groupStatus); + } + for (auto& pdisk: *pbConfig->mutable_pdisk()) { + pdisk.mutable_pdiskmetrics()->set_state(NKikimrBlobStorage::TPDiskState::Normal); + } + + auto groupId = GROUP_START_ID; + + auto group = pbConfig->add_group(); + group->CopyFrom(groupSample); + group->set_groupid(groupId); + group->set_operatingstatus(groupStatus); + group->set_erasurespecies(NHealthCheck::TSelfCheckRequest::BLOCK_4_2); + + group->clear_vslotid(); + auto vslotId = VCARD_START_ID; + + for (auto status: vdiskStatuses) { + auto vslot = pbConfig->add_vslot(); + vslot->CopyFrom(vslotSample); + vslot->set_vdiskidx(vslotId); + vslot->set_groupid(groupId); + vslot->set_failrealmidx(vslotId); + vslot->mutable_vslotid()->set_vslotid(vslotId); + + auto slotId = group->add_vslotid(); + slotId->CopyFrom(vslotIdSample); + slotId->set_vslotid(vslotId); + + const auto *descriptor = NKikimrBlobStorage::EVDiskStatus_descriptor(); + vslot->set_status(descriptor->FindValueByNumber(status)->name()); + + vslotId++; + } + }; + + void AddVSlotInVDiskStateResponse(TEvWhiteboard::TEvVDiskStateResponse::TPtr* ev, int groupCount, int vslotCount) { auto& pbRecord = (*ev)->Get()->Record; auto sample = pbRecord.vdiskstateinfo(0); @@ -491,6 +547,829 @@ Y_UNIT_TEST_SUITE(THealthCheckTest) { auto result = RequestHc(1, 100, false, true); CheckHcProtobufSizeIssue(result, Ydb::Monitoring::StatusFlag::RED, 1); } + + void ClearLoadAverage(TEvWhiteboard::TEvSystemStateResponse::TPtr* ev) { + auto *systemStateInfo = (*ev)->Get()->Record.MutableSystemStateInfo(); + for (NKikimrWhiteboard::TSystemStateInfo &state : *systemStateInfo) { + auto &loadAverage = *state.MutableLoadAverage(); + for (int i = 0; i < loadAverage.size(); ++i) { + loadAverage[i] = 0; + } + } + } + + const TPathId SERVERLESS_DOMAIN_KEY = {7000000000, 2}; + const TPathId SHARED_DOMAIN_KEY = {7000000000, 1}; + const TString SHARED_STORAGE_POOL_NAME = "/Root/shared:test"; + + void AddSharedGroupInControllerSelectGroupsResult(TEvBlobStorage::TEvControllerSelectGroupsResult::TPtr* ev) { + auto& pbRecord = (*ev)->Get()->Record; + auto pbMatchGroups = pbRecord.add_matchinggroups(); + + auto group = pbMatchGroups->add_groups(); + group->set_groupid(GROUP_START_ID); + group->set_storagepoolname(SHARED_STORAGE_POOL_NAME); + } + + void ChangeNavigateKeyResultServerless(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr* ev, + NKikimrSubDomains::EServerlessComputeResourcesMode serverlessComputeResourcesMode, + TTestActorRuntime& runtime) + { + TSchemeCacheNavigate::TEntry& entry((*ev)->Get()->Request->ResultSet.front()); + TString path = CanonizePath(entry.Path); + if (path == "/Root/serverless" || entry.TableId.PathId == SERVERLESS_DOMAIN_KEY) { + entry.Status = TSchemeCacheNavigate::EStatus::Ok; + entry.Kind = TSchemeCacheNavigate::EKind::KindExtSubdomain; + entry.Path = {"Root", "serverless"}; + entry.DomainInfo = MakeIntrusive<TDomainInfo>(SERVERLESS_DOMAIN_KEY, SHARED_DOMAIN_KEY); + entry.DomainInfo->ServerlessComputeResourcesMode = serverlessComputeResourcesMode; + } else if (path == "/Root/shared" || entry.TableId.PathId == SHARED_DOMAIN_KEY) { + entry.Status = TSchemeCacheNavigate::EStatus::Ok; + entry.Kind = TSchemeCacheNavigate::EKind::KindExtSubdomain; + entry.Path = {"Root", "shared"}; + entry.DomainInfo = MakeIntrusive<TDomainInfo>(SHARED_DOMAIN_KEY, SHARED_DOMAIN_KEY); + auto domains = runtime.GetAppData().DomainsInfo; + auto domain = domains->Domains.begin()->second; + ui64 hiveId = domains->GetHive(domain->DefaultHiveUid); + entry.DomainInfo->Params.SetHive(hiveId); + } + } + + void ChangeDescribeSchemeResultServerless(NSchemeShard::TEvSchemeShard::TEvDescribeSchemeResult::TPtr* ev) { + auto record = (*ev)->Get()->MutableRecord(); + if (record->path() == "/Root/serverless" || record->path() == "/Root/shared") { + record->set_status(NKikimrScheme::StatusSuccess); + auto pool = record->mutable_pathdescription()->mutable_domaindescription()->add_storagepools(); + pool->set_name(SHARED_STORAGE_POOL_NAME); + pool->set_kind("kind"); + } + } + + void ChangeGetTenantStatusResponse(NConsole::TEvConsole::TEvGetTenantStatusResponse::TPtr* ev, const TString &path) { + auto *operation = (*ev)->Get()->Record.MutableResponse()->mutable_operation(); + Ydb::Cms::GetDatabaseStatusResult getTenantStatusResult; + getTenantStatusResult.set_path(path); + operation->mutable_result()->PackFrom(getTenantStatusResult); + operation->set_status(Ydb::StatusIds::SUCCESS); + } + + void AddPathsToListTenantsResponse(NConsole::TEvConsole::TEvListTenantsResponse::TPtr* ev, const TVector<TString> &paths) { + Ydb::Cms::ListDatabasesResult listTenantsResult; + (*ev)->Get()->Record.GetResponse().operation().result().UnpackTo(&listTenantsResult); + for (const auto &path : paths) { + listTenantsResult.Addpaths(path); + } + (*ev)->Get()->Record.MutableResponse()->mutable_operation()->mutable_result()->PackFrom(listTenantsResult); + } + + void ChangeResponseHiveNodeStats(TEvHive::TEvResponseHiveNodeStats::TPtr* ev, ui32 sharedDynNodeId = 0, + ui32 exclusiveDynNodeId = 0) + { + auto &record = (*ev)->Get()->Record; + if (sharedDynNodeId) { + auto *sharedNodeStats = record.MutableNodeStats()->Add(); + sharedNodeStats->SetNodeId(sharedDynNodeId); + sharedNodeStats->MutableNodeDomain()->SetSchemeShard(SHARED_DOMAIN_KEY.OwnerId); + sharedNodeStats->MutableNodeDomain()->SetPathId(SHARED_DOMAIN_KEY.LocalPathId); + } + + if (exclusiveDynNodeId) { + auto *exclusiveNodeStats = record.MutableNodeStats()->Add(); + exclusiveNodeStats->SetNodeId(exclusiveDynNodeId); + exclusiveNodeStats->MutableNodeDomain()->SetSchemeShard(SERVERLESS_DOMAIN_KEY.OwnerId); + exclusiveNodeStats->MutableNodeDomain()->SetPathId(SERVERLESS_DOMAIN_KEY.LocalPathId); + } + } + + Y_UNIT_TEST(SpecificServerless) { + TPortManager tp; + ui16 port = tp.GetPort(2134); + ui16 grpcPort = tp.GetPort(2135); + auto settings = TServerSettings(port) + .SetNodeCount(1) + .SetDynamicNodeCount(1) + .SetUseRealThreads(false) + .SetDomainName("Root"); + TServer server(settings); + server.EnableGRpc(grpcPort); + TClient client(settings); + TTestActorRuntime& runtime = *server.GetRuntime(); + + auto &dynamicNameserviceConfig = runtime.GetAppData().DynamicNameserviceConfig; + dynamicNameserviceConfig->MaxStaticNodeId = runtime.GetNodeId(server.StaticNodes() - 1); + dynamicNameserviceConfig->MinDynamicNodeId = runtime.GetNodeId(server.StaticNodes()); + dynamicNameserviceConfig->MaxDynamicNodeId = runtime.GetNodeId(server.StaticNodes() + server.DynamicNodes() - 1); + + ui32 sharedDynNodeId = runtime.GetNodeId(1); + + auto observerFunc = [&](TAutoPtr<IEventHandle>& ev) { + switch (ev->GetTypeRewrite()) { + case NConsole::TEvConsole::EvGetTenantStatusResponse: { + auto *x = reinterpret_cast<NConsole::TEvConsole::TEvGetTenantStatusResponse::TPtr*>(&ev); + ChangeGetTenantStatusResponse(x, "/Root/serverless"); + break; + } + case TEvTxProxySchemeCache::EvNavigateKeySetResult: { + auto *x = reinterpret_cast<TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr*>(&ev); + ChangeNavigateKeyResultServerless(x, NKikimrSubDomains::EServerlessComputeResourcesModeShared, runtime); + break; + } + case TEvHive::EvResponseHiveNodeStats: { + auto *x = reinterpret_cast<TEvHive::TEvResponseHiveNodeStats::TPtr*>(&ev); + ChangeResponseHiveNodeStats(x, sharedDynNodeId); + break; + } + case TEvSchemeShard::EvDescribeSchemeResult: { + auto *x = reinterpret_cast<NSchemeShard::TEvSchemeShard::TEvDescribeSchemeResult::TPtr*>(&ev); + ChangeDescribeSchemeResultServerless(x); + break; + } + case TEvBlobStorage::EvControllerSelectGroupsResult: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerSelectGroupsResult::TPtr*>(&ev); + AddSharedGroupInControllerSelectGroupsResult(x); + break; + } + case TEvBlobStorage::EvControllerConfigResponse: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerConfigResponse::TPtr*>(&ev); + TVector<NKikimrBlobStorage::EVDiskStatus> vdiskStatuses = { NKikimrBlobStorage::EVDiskStatus::READY }; + AddGroupVSlotInControllerConfigResponseWithStaticGroup(x, NKikimrBlobStorage::TGroupStatus::FULL, vdiskStatuses); + break; + } + case TEvWhiteboard::EvSystemStateResponse: { + auto *x = reinterpret_cast<TEvWhiteboard::TEvSystemStateResponse::TPtr*>(&ev); + ClearLoadAverage(x); + break; + } + } + + return TTestActorRuntime::EEventAction::PROCESS; + }; + runtime.SetObserverFunc(observerFunc); + + TActorId sender = runtime.AllocateEdgeActor(); + TAutoPtr<IEventHandle> handle; + + auto *request = new NHealthCheck::TEvSelfCheckRequest; + request->Request.set_return_verbose_status(true); + request->Database = "/Root/serverless"; + runtime.Send(new IEventHandle(NHealthCheck::MakeHealthCheckID(), sender, request, 0)); + const auto result = runtime.GrabEdgeEvent<NHealthCheck::TEvSelfCheckResult>(handle)->Result; + Ctest << result.ShortDebugString(); + UNIT_ASSERT_VALUES_EQUAL(result.self_check_result(), Ydb::Monitoring::SelfCheck::GOOD); + + UNIT_ASSERT_VALUES_EQUAL(result.database_status_size(), 1); + const auto &database_status = result.database_status(0); + UNIT_ASSERT_VALUES_EQUAL(database_status.name(), "/Root/serverless"); + UNIT_ASSERT_VALUES_EQUAL(database_status.overall(), Ydb::Monitoring::StatusFlag::GREEN); + + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes()[0].id(), ToString(sharedDynNodeId)); + + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools()[0].id(), SHARED_STORAGE_POOL_NAME); + } + + Y_UNIT_TEST(SpecificServerlessWithExclusiveNodes) { + TPortManager tp; + ui16 port = tp.GetPort(2134); + ui16 grpcPort = tp.GetPort(2135); + auto settings = TServerSettings(port) + .SetNodeCount(1) + .SetDynamicNodeCount(2) + .SetUseRealThreads(false) + .SetDomainName("Root"); + TServer server(settings); + server.EnableGRpc(grpcPort); + TClient client(settings); + TTestActorRuntime& runtime = *server.GetRuntime(); + + auto &dynamicNameserviceConfig = runtime.GetAppData().DynamicNameserviceConfig; + dynamicNameserviceConfig->MaxStaticNodeId = runtime.GetNodeId(server.StaticNodes() - 1); + dynamicNameserviceConfig->MinDynamicNodeId = runtime.GetNodeId(server.StaticNodes()); + dynamicNameserviceConfig->MaxDynamicNodeId = runtime.GetNodeId(server.StaticNodes() + server.DynamicNodes() - 1); + + ui32 sharedDynNodeId = runtime.GetNodeId(1); + ui32 exclusiveDynNodeId = runtime.GetNodeId(2); + + auto observerFunc = [&](TAutoPtr<IEventHandle>& ev) { + switch (ev->GetTypeRewrite()) { + case NConsole::TEvConsole::EvGetTenantStatusResponse: { + auto *x = reinterpret_cast<NConsole::TEvConsole::TEvGetTenantStatusResponse::TPtr*>(&ev); + ChangeGetTenantStatusResponse(x, "/Root/serverless"); + break; + } + case TEvTxProxySchemeCache::EvNavigateKeySetResult: { + auto *x = reinterpret_cast<TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr*>(&ev); + ChangeNavigateKeyResultServerless(x, NKikimrSubDomains::EServerlessComputeResourcesModeExclusive, runtime); + break; + } + case TEvHive::EvResponseHiveNodeStats: { + auto *x = reinterpret_cast<TEvHive::TEvResponseHiveNodeStats::TPtr*>(&ev); + ChangeResponseHiveNodeStats(x, sharedDynNodeId, exclusiveDynNodeId); + break; + } + case TEvSchemeShard::EvDescribeSchemeResult: { + auto *x = reinterpret_cast<NSchemeShard::TEvSchemeShard::TEvDescribeSchemeResult::TPtr*>(&ev); + ChangeDescribeSchemeResultServerless(x); + break; + } + case TEvBlobStorage::EvControllerSelectGroupsResult: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerSelectGroupsResult::TPtr*>(&ev); + AddSharedGroupInControllerSelectGroupsResult(x); + break; + } + case TEvBlobStorage::EvControllerConfigResponse: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerConfigResponse::TPtr*>(&ev); + TVector<NKikimrBlobStorage::EVDiskStatus> vdiskStatuses = { NKikimrBlobStorage::EVDiskStatus::READY }; + AddGroupVSlotInControllerConfigResponseWithStaticGroup(x, NKikimrBlobStorage::TGroupStatus::FULL, vdiskStatuses); + break; + } + case TEvWhiteboard::EvSystemStateResponse: { + auto *x = reinterpret_cast<TEvWhiteboard::TEvSystemStateResponse::TPtr*>(&ev); + ClearLoadAverage(x); + break; + } + } + + return TTestActorRuntime::EEventAction::PROCESS; + }; + runtime.SetObserverFunc(observerFunc); + + TActorId sender = runtime.AllocateEdgeActor(); + TAutoPtr<IEventHandle> handle; + + auto *request = new NHealthCheck::TEvSelfCheckRequest; + request->Request.set_return_verbose_status(true); + request->Database = "/Root/serverless"; + runtime.Send(new IEventHandle(NHealthCheck::MakeHealthCheckID(), sender, request, 0)); + const auto result = runtime.GrabEdgeEvent<NHealthCheck::TEvSelfCheckResult>(handle)->Result; + + Ctest << result.ShortDebugString(); + UNIT_ASSERT_VALUES_EQUAL(result.self_check_result(), Ydb::Monitoring::SelfCheck::GOOD); + + UNIT_ASSERT_VALUES_EQUAL(result.database_status_size(), 1); + const auto &database_status = result.database_status(0); + UNIT_ASSERT_VALUES_EQUAL(database_status.name(), "/Root/serverless"); + UNIT_ASSERT_VALUES_EQUAL(database_status.overall(), Ydb::Monitoring::StatusFlag::GREEN); + + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes()[0].id(), ToString(exclusiveDynNodeId)); + + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools()[0].id(), SHARED_STORAGE_POOL_NAME); + } + + Y_UNIT_TEST(IgnoreServerlessWhenNotSpecific) { + TPortManager tp; + ui16 port = tp.GetPort(2134); + ui16 grpcPort = tp.GetPort(2135); + auto settings = TServerSettings(port) + .SetNodeCount(1) + .SetDynamicNodeCount(1) + .SetUseRealThreads(false) + .SetDomainName("Root"); + TServer server(settings); + server.EnableGRpc(grpcPort); + TClient client(settings); + TTestActorRuntime& runtime = *server.GetRuntime(); + + auto &dynamicNameserviceConfig = runtime.GetAppData().DynamicNameserviceConfig; + dynamicNameserviceConfig->MaxStaticNodeId = runtime.GetNodeId(server.StaticNodes() - 1); + dynamicNameserviceConfig->MinDynamicNodeId = runtime.GetNodeId(server.StaticNodes()); + dynamicNameserviceConfig->MaxDynamicNodeId = runtime.GetNodeId(server.StaticNodes() + server.DynamicNodes() - 1); + + ui32 sharedDynNodeId = runtime.GetNodeId(1); + + bool firstConsoleResponse = true; + auto observerFunc = [&](TAutoPtr<IEventHandle>& ev) { + switch (ev->GetTypeRewrite()) { + case NConsole::TEvConsole::EvListTenantsResponse: { + auto *x = reinterpret_cast<NConsole::TEvConsole::TEvListTenantsResponse::TPtr*>(&ev); + AddPathsToListTenantsResponse(x, { "/Root/serverless", "/Root/shared" }); + break; + } + case NConsole::TEvConsole::EvGetTenantStatusResponse: { + auto *x = reinterpret_cast<NConsole::TEvConsole::TEvGetTenantStatusResponse::TPtr*>(&ev); + if (firstConsoleResponse) { + firstConsoleResponse = false; + ChangeGetTenantStatusResponse(x, "/Root/serverless"); + } else { + ChangeGetTenantStatusResponse(x, "/Root/shared"); + } + break; + } + case TEvTxProxySchemeCache::EvNavigateKeySetResult: { + auto *x = reinterpret_cast<TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr*>(&ev); + ChangeNavigateKeyResultServerless(x, NKikimrSubDomains::EServerlessComputeResourcesModeShared, runtime); + break; + } + case TEvHive::EvResponseHiveNodeStats: { + auto *x = reinterpret_cast<TEvHive::TEvResponseHiveNodeStats::TPtr*>(&ev); + ChangeResponseHiveNodeStats(x, sharedDynNodeId); + break; + } + case TEvSchemeShard::EvDescribeSchemeResult: { + auto *x = reinterpret_cast<NSchemeShard::TEvSchemeShard::TEvDescribeSchemeResult::TPtr*>(&ev); + ChangeDescribeSchemeResultServerless(x); + break; + } + case TEvBlobStorage::EvControllerSelectGroupsResult: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerSelectGroupsResult::TPtr*>(&ev); + AddSharedGroupInControllerSelectGroupsResult(x); + break; + } + case TEvBlobStorage::EvControllerConfigResponse: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerConfigResponse::TPtr*>(&ev); + TVector<NKikimrBlobStorage::EVDiskStatus> vdiskStatuses = { NKikimrBlobStorage::EVDiskStatus::READY }; + AddGroupVSlotInControllerConfigResponseWithStaticGroup(x, NKikimrBlobStorage::TGroupStatus::FULL, vdiskStatuses); + break; + } + } + + return TTestActorRuntime::EEventAction::PROCESS; + }; + runtime.SetObserverFunc(observerFunc); + + TActorId sender = runtime.AllocateEdgeActor(); + TAutoPtr<IEventHandle> handle; + + auto *request = new NHealthCheck::TEvSelfCheckRequest; + request->Request.set_return_verbose_status(true); + runtime.Send(new IEventHandle(NHealthCheck::MakeHealthCheckID(), sender, request, 0)); + const auto result = runtime.GrabEdgeEvent<NHealthCheck::TEvSelfCheckResult>(handle)->Result; + Ctest << result.ShortDebugString(); + UNIT_ASSERT_VALUES_EQUAL(result.self_check_result(), Ydb::Monitoring::SelfCheck::GOOD); + + bool databaseFoundInResult = false; + for (const auto &database_status : result.database_status()) { + if (database_status.name() == "/Root/serverless") { + databaseFoundInResult = true; + } + } + UNIT_ASSERT(!databaseFoundInResult); + } + + Y_UNIT_TEST(DontIgnoreServerlessWithExclusiveNodesWhenNotSpecific) { + TPortManager tp; + ui16 port = tp.GetPort(2134); + ui16 grpcPort = tp.GetPort(2135); + auto settings = TServerSettings(port) + .SetNodeCount(1) + .SetDynamicNodeCount(2) + .SetUseRealThreads(false) + .SetDomainName("Root"); + TServer server(settings); + server.EnableGRpc(grpcPort); + TClient client(settings); + TTestActorRuntime& runtime = *server.GetRuntime(); + + auto &dynamicNameserviceConfig = runtime.GetAppData().DynamicNameserviceConfig; + dynamicNameserviceConfig->MaxStaticNodeId = runtime.GetNodeId(server.StaticNodes() - 1); + dynamicNameserviceConfig->MinDynamicNodeId = runtime.GetNodeId(server.StaticNodes()); + dynamicNameserviceConfig->MaxDynamicNodeId = runtime.GetNodeId(server.StaticNodes() + server.DynamicNodes() - 1); + + ui32 sharedDynNodeId = runtime.GetNodeId(1); + ui32 exclusiveDynNodeId = runtime.GetNodeId(2); + + bool firstConsoleResponse = true; + auto observerFunc = [&](TAutoPtr<IEventHandle>& ev) { + switch (ev->GetTypeRewrite()) { + case NConsole::TEvConsole::EvListTenantsResponse: { + auto *x = reinterpret_cast<NConsole::TEvConsole::TEvListTenantsResponse::TPtr*>(&ev); + AddPathsToListTenantsResponse(x, { "/Root/serverless", "/Root/shared" }); + break; + } + case NConsole::TEvConsole::EvGetTenantStatusResponse: { + auto *x = reinterpret_cast<NConsole::TEvConsole::TEvGetTenantStatusResponse::TPtr*>(&ev); + if (firstConsoleResponse) { + firstConsoleResponse = false; + ChangeGetTenantStatusResponse(x, "/Root/serverless"); + } else { + ChangeGetTenantStatusResponse(x, "/Root/shared"); + } + break; + } + case TEvTxProxySchemeCache::EvNavigateKeySetResult: { + auto *x = reinterpret_cast<TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr*>(&ev); + ChangeNavigateKeyResultServerless(x, NKikimrSubDomains::EServerlessComputeResourcesModeExclusive, runtime); + break; + } + case TEvHive::EvResponseHiveNodeStats: { + auto *x = reinterpret_cast<TEvHive::TEvResponseHiveNodeStats::TPtr*>(&ev); + ChangeResponseHiveNodeStats(x, sharedDynNodeId, exclusiveDynNodeId); + break; + } + case TEvSchemeShard::EvDescribeSchemeResult: { + auto *x = reinterpret_cast<NSchemeShard::TEvSchemeShard::TEvDescribeSchemeResult::TPtr*>(&ev); + ChangeDescribeSchemeResultServerless(x); + break; + } + case TEvBlobStorage::EvControllerSelectGroupsResult: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerSelectGroupsResult::TPtr*>(&ev); + AddSharedGroupInControllerSelectGroupsResult(x); + break; + } + case TEvBlobStorage::EvControllerConfigResponse: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerConfigResponse::TPtr*>(&ev); + TVector<NKikimrBlobStorage::EVDiskStatus> vdiskStatuses = { NKikimrBlobStorage::EVDiskStatus::READY }; + AddGroupVSlotInControllerConfigResponseWithStaticGroup(x, NKikimrBlobStorage::TGroupStatus::FULL, vdiskStatuses); + break; + } + case TEvWhiteboard::EvSystemStateResponse: { + auto *x = reinterpret_cast<TEvWhiteboard::TEvSystemStateResponse::TPtr*>(&ev); + ClearLoadAverage(x); + break; + } + } + + return TTestActorRuntime::EEventAction::PROCESS; + }; + runtime.SetObserverFunc(observerFunc); + + TActorId sender = runtime.AllocateEdgeActor(); + TAutoPtr<IEventHandle> handle; + + auto *request = new NHealthCheck::TEvSelfCheckRequest; + request->Request.set_return_verbose_status(true); + runtime.Send(new IEventHandle(NHealthCheck::MakeHealthCheckID(), sender, request, 0)); + const auto result = runtime.GrabEdgeEvent<NHealthCheck::TEvSelfCheckResult>(handle)->Result; + + Ctest << result.ShortDebugString(); + UNIT_ASSERT_VALUES_EQUAL(result.self_check_result(), Ydb::Monitoring::SelfCheck::GOOD); + + bool databaseFoundInResult = false; + for (const auto &database_status : result.database_status()) { + if (database_status.name() == "/Root/serverless") { + databaseFoundInResult = true; + UNIT_ASSERT_VALUES_EQUAL(database_status.overall(), Ydb::Monitoring::StatusFlag::GREEN); + + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes()[0].id(), ToString(exclusiveDynNodeId)); + + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools()[0].id(), SHARED_STORAGE_POOL_NAME); + } + } + UNIT_ASSERT(databaseFoundInResult); + } + + Y_UNIT_TEST(ServerlessWhenTroublesWithSharedNodes) { + TPortManager tp; + ui16 port = tp.GetPort(2134); + ui16 grpcPort = tp.GetPort(2135); + auto settings = TServerSettings(port) + .SetNodeCount(1) + .SetUseRealThreads(false) + .SetDomainName("Root"); + TServer server(settings); + server.EnableGRpc(grpcPort); + TClient client(settings); + TTestActorRuntime& runtime = *server.GetRuntime(); + + auto &dynamicNameserviceConfig = runtime.GetAppData().DynamicNameserviceConfig; + dynamicNameserviceConfig->MaxStaticNodeId = runtime.GetNodeId(server.StaticNodes() - 1); + + auto observerFunc = [&](TAutoPtr<IEventHandle>& ev) { + switch (ev->GetTypeRewrite()) { + case NConsole::TEvConsole::EvGetTenantStatusResponse: { + auto *x = reinterpret_cast<NConsole::TEvConsole::TEvGetTenantStatusResponse::TPtr*>(&ev); + ChangeGetTenantStatusResponse(x, "/Root/serverless"); + break; + } + case TEvTxProxySchemeCache::EvNavigateKeySetResult: { + auto *x = reinterpret_cast<TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr*>(&ev); + ChangeNavigateKeyResultServerless(x, NKikimrSubDomains::EServerlessComputeResourcesModeShared, runtime); + break; + } + case TEvSchemeShard::EvDescribeSchemeResult: { + auto *x = reinterpret_cast<NSchemeShard::TEvSchemeShard::TEvDescribeSchemeResult::TPtr*>(&ev); + ChangeDescribeSchemeResultServerless(x); + break; + } + case TEvBlobStorage::EvControllerSelectGroupsResult: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerSelectGroupsResult::TPtr*>(&ev); + AddSharedGroupInControllerSelectGroupsResult(x); + break; + } + case TEvBlobStorage::EvControllerConfigResponse: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerConfigResponse::TPtr*>(&ev); + TVector<NKikimrBlobStorage::EVDiskStatus> vdiskStatuses = { NKikimrBlobStorage::EVDiskStatus::READY }; + AddGroupVSlotInControllerConfigResponseWithStaticGroup(x, NKikimrBlobStorage::TGroupStatus::FULL, vdiskStatuses); + break; + } + case TEvWhiteboard::EvSystemStateResponse: { + auto *x = reinterpret_cast<TEvWhiteboard::TEvSystemStateResponse::TPtr*>(&ev); + ClearLoadAverage(x); + break; + } + } + + return TTestActorRuntime::EEventAction::PROCESS; + }; + runtime.SetObserverFunc(observerFunc); + + TActorId sender = runtime.AllocateEdgeActor(); + TAutoPtr<IEventHandle> handle; + + auto *request = new NHealthCheck::TEvSelfCheckRequest; + request->Request.set_return_verbose_status(true); + request->Database = "/Root/serverless"; + runtime.Send(new IEventHandle(NHealthCheck::MakeHealthCheckID(), sender, request, 0)); + const auto result = runtime.GrabEdgeEvent<NHealthCheck::TEvSelfCheckResult>(handle)->Result; + + Ctest << result.ShortDebugString(); + UNIT_ASSERT_VALUES_EQUAL(result.self_check_result(), Ydb::Monitoring::SelfCheck::EMERGENCY); + + UNIT_ASSERT_VALUES_EQUAL(result.database_status_size(), 1); + const auto &database_status = result.database_status(0); + UNIT_ASSERT_VALUES_EQUAL(database_status.name(), "/Root/serverless"); + UNIT_ASSERT_VALUES_EQUAL(database_status.overall(), Ydb::Monitoring::StatusFlag::RED); + + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().overall(), Ydb::Monitoring::StatusFlag::RED); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes().size(), 0); + + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools()[0].id(), SHARED_STORAGE_POOL_NAME); + } + + Y_UNIT_TEST(ServerlessWithExclusiveNodesWhenTroublesWithSharedNodes) { + TPortManager tp; + ui16 port = tp.GetPort(2134); + ui16 grpcPort = tp.GetPort(2135); + auto settings = TServerSettings(port) + .SetNodeCount(1) + .SetDynamicNodeCount(1) + .SetUseRealThreads(false) + .SetDomainName("Root"); + TServer server(settings); + server.EnableGRpc(grpcPort); + TClient client(settings); + TTestActorRuntime& runtime = *server.GetRuntime(); + + auto &dynamicNameserviceConfig = runtime.GetAppData().DynamicNameserviceConfig; + dynamicNameserviceConfig->MaxStaticNodeId = runtime.GetNodeId(server.StaticNodes() - 1); + dynamicNameserviceConfig->MinDynamicNodeId = runtime.GetNodeId(server.StaticNodes()); + dynamicNameserviceConfig->MaxDynamicNodeId = runtime.GetNodeId(server.StaticNodes() + server.DynamicNodes() - 1); + + ui32 staticNode = runtime.GetNodeId(0); + ui32 exclusiveDynNodeId = runtime.GetNodeId(1); + + bool firstConsoleResponse = true; + auto observerFunc = [&](TAutoPtr<IEventHandle>& ev) { + switch (ev->GetTypeRewrite()) { + case NConsole::TEvConsole::EvListTenantsResponse: { + auto *x = reinterpret_cast<NConsole::TEvConsole::TEvListTenantsResponse::TPtr*>(&ev); + AddPathsToListTenantsResponse(x, { "/Root/serverless", "/Root/shared" }); + break; + } + case NConsole::TEvConsole::EvGetTenantStatusResponse: { + auto *x = reinterpret_cast<NConsole::TEvConsole::TEvGetTenantStatusResponse::TPtr*>(&ev); + if (firstConsoleResponse) { + firstConsoleResponse = false; + ChangeGetTenantStatusResponse(x, "/Root/serverless"); + } else { + ChangeGetTenantStatusResponse(x, "/Root/shared"); + } + break; + } + case TEvTxProxySchemeCache::EvNavigateKeySetResult: { + auto *x = reinterpret_cast<TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr*>(&ev); + ChangeNavigateKeyResultServerless(x, NKikimrSubDomains::EServerlessComputeResourcesModeExclusive, runtime); + break; + } + case TEvHive::EvResponseHiveNodeStats: { + auto *x = reinterpret_cast<TEvHive::TEvResponseHiveNodeStats::TPtr*>(&ev); + ChangeResponseHiveNodeStats(x, 0, exclusiveDynNodeId); + break; + } + case TEvSchemeShard::EvDescribeSchemeResult: { + auto *x = reinterpret_cast<NSchemeShard::TEvSchemeShard::TEvDescribeSchemeResult::TPtr*>(&ev); + ChangeDescribeSchemeResultServerless(x); + break; + } + case TEvBlobStorage::EvControllerSelectGroupsResult: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerSelectGroupsResult::TPtr*>(&ev); + AddSharedGroupInControllerSelectGroupsResult(x); + break; + } + case TEvBlobStorage::EvControllerConfigResponse: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerConfigResponse::TPtr*>(&ev); + TVector<NKikimrBlobStorage::EVDiskStatus> vdiskStatuses = { NKikimrBlobStorage::EVDiskStatus::READY }; + AddGroupVSlotInControllerConfigResponseWithStaticGroup(x, NKikimrBlobStorage::TGroupStatus::FULL, vdiskStatuses); + break; + } + case TEvWhiteboard::EvSystemStateResponse: { + auto *x = reinterpret_cast<TEvWhiteboard::TEvSystemStateResponse::TPtr*>(&ev); + ClearLoadAverage(x); + break; + } + } + + return TTestActorRuntime::EEventAction::PROCESS; + }; + runtime.SetObserverFunc(observerFunc); + + TActorId sender = runtime.AllocateEdgeActor(); + TAutoPtr<IEventHandle> handle; + + auto *request = new NHealthCheck::TEvSelfCheckRequest; + request->Request.set_return_verbose_status(true); + runtime.Send(new IEventHandle(NHealthCheck::MakeHealthCheckID(), sender, request, 0)); + const auto result = runtime.GrabEdgeEvent<NHealthCheck::TEvSelfCheckResult>(handle)->Result; + + Ctest << result.ShortDebugString(); + UNIT_ASSERT_VALUES_EQUAL(result.self_check_result(), Ydb::Monitoring::SelfCheck::EMERGENCY); + + bool serverlessDatabaseFoundInResult = false; + bool sharedDatabaseFoundInResult = false; + bool rootDatabaseFoundInResult = false; + + UNIT_ASSERT_VALUES_EQUAL(result.database_status_size(), 3); + for (const auto &database_status : result.database_status()) { + if (database_status.name() == "/Root/serverless") { + serverlessDatabaseFoundInResult = true; + + UNIT_ASSERT_VALUES_EQUAL(database_status.overall(), Ydb::Monitoring::StatusFlag::GREEN); + + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes()[0].id(), ToString(exclusiveDynNodeId)); + + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools()[0].id(), SHARED_STORAGE_POOL_NAME); + } else if (database_status.name() == "/Root/shared") { + sharedDatabaseFoundInResult = true; + + UNIT_ASSERT_VALUES_EQUAL(database_status.overall(), Ydb::Monitoring::StatusFlag::RED); + + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().overall(), Ydb::Monitoring::StatusFlag::RED); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes().size(), 0); + + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools()[0].id(), SHARED_STORAGE_POOL_NAME); + } else if (database_status.name() == "/Root") { + rootDatabaseFoundInResult = true; + + UNIT_ASSERT_VALUES_EQUAL(database_status.overall(), Ydb::Monitoring::StatusFlag::GREEN); + + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes()[0].id(), ToString(staticNode)); + + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools()[0].id(), "static"); + } + } + UNIT_ASSERT(serverlessDatabaseFoundInResult); + UNIT_ASSERT(sharedDatabaseFoundInResult); + UNIT_ASSERT(rootDatabaseFoundInResult); + } + + Y_UNIT_TEST(SharedWhenTroublesWithExclusiveNodes) { + TPortManager tp; + ui16 port = tp.GetPort(2134); + ui16 grpcPort = tp.GetPort(2135); + auto settings = TServerSettings(port) + .SetNodeCount(1) + .SetDynamicNodeCount(1) + .SetUseRealThreads(false) + .SetDomainName("Root"); + TServer server(settings); + server.EnableGRpc(grpcPort); + TClient client(settings); + TTestActorRuntime& runtime = *server.GetRuntime(); + + auto &dynamicNameserviceConfig = runtime.GetAppData().DynamicNameserviceConfig; + dynamicNameserviceConfig->MaxStaticNodeId = runtime.GetNodeId(server.StaticNodes() - 1); + dynamicNameserviceConfig->MinDynamicNodeId = runtime.GetNodeId(server.StaticNodes()); + dynamicNameserviceConfig->MaxDynamicNodeId = runtime.GetNodeId(server.StaticNodes() + server.DynamicNodes() - 1); + + ui32 staticNode = runtime.GetNodeId(0); + ui32 sharedDynNodeId = runtime.GetNodeId(1); + + bool firstConsoleResponse = true; + auto observerFunc = [&](TAutoPtr<IEventHandle>& ev) { + switch (ev->GetTypeRewrite()) { + case NConsole::TEvConsole::EvListTenantsResponse: { + auto *x = reinterpret_cast<NConsole::TEvConsole::TEvListTenantsResponse::TPtr*>(&ev); + AddPathsToListTenantsResponse(x, { "/Root/serverless", "/Root/shared" }); + break; + } + case NConsole::TEvConsole::EvGetTenantStatusResponse: { + auto *x = reinterpret_cast<NConsole::TEvConsole::TEvGetTenantStatusResponse::TPtr*>(&ev); + if (firstConsoleResponse) { + firstConsoleResponse = false; + ChangeGetTenantStatusResponse(x, "/Root/serverless"); + } else { + ChangeGetTenantStatusResponse(x, "/Root/shared"); + } + break; + } + case TEvTxProxySchemeCache::EvNavigateKeySetResult: { + auto *x = reinterpret_cast<TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr*>(&ev); + ChangeNavigateKeyResultServerless(x, NKikimrSubDomains::EServerlessComputeResourcesModeExclusive, runtime); + break; + } + case TEvHive::EvResponseHiveNodeStats: { + auto *x = reinterpret_cast<TEvHive::TEvResponseHiveNodeStats::TPtr*>(&ev); + ChangeResponseHiveNodeStats(x, sharedDynNodeId); + break; + } + case TEvSchemeShard::EvDescribeSchemeResult: { + auto *x = reinterpret_cast<NSchemeShard::TEvSchemeShard::TEvDescribeSchemeResult::TPtr*>(&ev); + ChangeDescribeSchemeResultServerless(x); + break; + } + case TEvBlobStorage::EvControllerSelectGroupsResult: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerSelectGroupsResult::TPtr*>(&ev); + AddSharedGroupInControllerSelectGroupsResult(x); + break; + } + case TEvBlobStorage::EvControllerConfigResponse: { + auto *x = reinterpret_cast<TEvBlobStorage::TEvControllerConfigResponse::TPtr*>(&ev); + TVector<NKikimrBlobStorage::EVDiskStatus> vdiskStatuses = { NKikimrBlobStorage::EVDiskStatus::READY }; + AddGroupVSlotInControllerConfigResponseWithStaticGroup(x, NKikimrBlobStorage::TGroupStatus::FULL, vdiskStatuses); + break; + } + case TEvWhiteboard::EvSystemStateResponse: { + auto *x = reinterpret_cast<TEvWhiteboard::TEvSystemStateResponse::TPtr*>(&ev); + ClearLoadAverage(x); + break; + } + } + + return TTestActorRuntime::EEventAction::PROCESS; + }; + runtime.SetObserverFunc(observerFunc); + + TActorId sender = runtime.AllocateEdgeActor(); + TAutoPtr<IEventHandle> handle; + + auto *request = new NHealthCheck::TEvSelfCheckRequest; + request->Request.set_return_verbose_status(true); + runtime.Send(new IEventHandle(NHealthCheck::MakeHealthCheckID(), sender, request, 0)); + const auto result = runtime.GrabEdgeEvent<NHealthCheck::TEvSelfCheckResult>(handle)->Result; + + Ctest << result.ShortDebugString(); + UNIT_ASSERT_VALUES_EQUAL(result.self_check_result(), Ydb::Monitoring::SelfCheck::EMERGENCY); + + bool serverlessDatabaseFoundInResult = false; + bool sharedDatabaseFoundInResult = false; + bool rootDatabaseFoundInResult = false; + + UNIT_ASSERT_VALUES_EQUAL(result.database_status_size(), 3); + for (const auto &database_status : result.database_status()) { + if (database_status.name() == "/Root/serverless") { + serverlessDatabaseFoundInResult = true; + + UNIT_ASSERT_VALUES_EQUAL(database_status.overall(), Ydb::Monitoring::StatusFlag::RED); + + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().overall(), Ydb::Monitoring::StatusFlag::RED); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes().size(), 0); + + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools()[0].id(), SHARED_STORAGE_POOL_NAME); + } else if (database_status.name() == "/Root/shared") { + sharedDatabaseFoundInResult = true; + + UNIT_ASSERT_VALUES_EQUAL(database_status.overall(), Ydb::Monitoring::StatusFlag::GREEN); + + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes()[0].id(), ToString(sharedDynNodeId)); + + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools()[0].id(), SHARED_STORAGE_POOL_NAME); + } else if (database_status.name() == "/Root") { + rootDatabaseFoundInResult = true; + + UNIT_ASSERT_VALUES_EQUAL(database_status.overall(), Ydb::Monitoring::StatusFlag::GREEN); + + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.compute().nodes()[0].id(), ToString(staticNode)); + + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().overall(), Ydb::Monitoring::StatusFlag::GREEN); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools().size(), 1); + UNIT_ASSERT_VALUES_EQUAL(database_status.storage().pools()[0].id(), "static"); + } + } + UNIT_ASSERT(serverlessDatabaseFoundInResult); + UNIT_ASSERT(sharedDatabaseFoundInResult); + UNIT_ASSERT(rootDatabaseFoundInResult); + } } } diff --git a/ydb/public/api/protos/out/out.cpp b/ydb/public/api/protos/out/out.cpp index 9e8b7263ee..aa1cae9304 100644 --- a/ydb/public/api/protos/out/out.cpp +++ b/ydb/public/api/protos/out/out.cpp @@ -1,4 +1,5 @@ #include <ydb/public/api/protos/ydb_cms.pb.h> +#include <ydb/public/api/protos/ydb_monitoring.pb.h> #include <ydb/public/api/protos/ydb_status_codes.pb.h> #include <util/stream/output.h> @@ -10,3 +11,11 @@ Y_DECLARE_OUT_SPEC(, Ydb::Cms::GetDatabaseStatusResult::State, stream, value) { Y_DECLARE_OUT_SPEC(, Ydb::StatusIds::StatusCode, stream, value) { stream << Ydb::StatusIds::StatusCode_Name(value); } + +Y_DECLARE_OUT_SPEC(, Ydb::Monitoring::SelfCheck::Result, stream, value) { + stream << Ydb::Monitoring::SelfCheck_Result_Name(value); +} + +Y_DECLARE_OUT_SPEC(, Ydb::Monitoring::StatusFlag::Status, stream, value) { + stream << Ydb::Monitoring::StatusFlag_Status_Name(value); +} |