aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIlia Shakhov <pixcc@ydb.tech>2024-01-30 15:53:55 +0300
committerGitHub <noreply@github.com>2024-01-30 15:53:55 +0300
commitae12905f2832ace12b9e9e480bd362d11f785b4d (patch)
tree37f478def50bf06ecde95f3cdb1ff4114aef0ff6
parent59147eb6ee5e83c58e051f84a7599cb60b83ccf4 (diff)
downloadydb-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.cpp51
-rw-r--r--ydb/core/health_check/health_check_ut.cpp881
-rw-r--r--ydb/public/api/protos/out/out.cpp9
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);
+}