Skip to content

Commit 9ed6a54

Browse files
authored
Merge 2d350f0 into ce6be6c
2 parents ce6be6c + 2d350f0 commit 9ed6a54

File tree

7 files changed

+167
-89
lines changed

7 files changed

+167
-89
lines changed

ydb/core/viewer/json_pipe_req.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,40 @@ TString TViewerPipeClient::GetPath(TEvTxProxySchemeCache::TEvNavigateKeySetResul
113113
return {};
114114
}
115115

116+
bool TViewerPipeClient::IsSuccess(const std::unique_ptr<TEvTxProxySchemeCache::TEvNavigateKeySetResult>& ev) {
117+
return (ev->Request->ResultSet.size() == 1) && (ev->Request->ResultSet.begin()->Status == NSchemeCache::TSchemeCacheNavigate::EStatus::Ok);
118+
}
119+
120+
TString TViewerPipeClient::GetError(const std::unique_ptr<TEvTxProxySchemeCache::TEvNavigateKeySetResult>& ev) {
121+
if (ev->Request->ResultSet.size() == 0) {
122+
return "empty response";
123+
}
124+
switch (ev->Request->ResultSet.begin()->Status) {
125+
case NSchemeCache::TSchemeCacheNavigate::EStatus::Ok:
126+
return "Ok";
127+
case NSchemeCache::TSchemeCacheNavigate::EStatus::Unknown:
128+
return "Unknown";
129+
case NSchemeCache::TSchemeCacheNavigate::EStatus::RootUnknown:
130+
return "RootUnknown";
131+
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathErrorUnknown:
132+
return "PathErrorUnknown";
133+
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathNotTable:
134+
return "PathNotTable";
135+
case NSchemeCache::TSchemeCacheNavigate::EStatus::PathNotPath:
136+
return "PathNotPath";
137+
case NSchemeCache::TSchemeCacheNavigate::EStatus::TableCreationNotComplete:
138+
return "TableCreationNotComplete";
139+
case NSchemeCache::TSchemeCacheNavigate::EStatus::LookupError:
140+
return "LookupError";
141+
case NSchemeCache::TSchemeCacheNavigate::EStatus::RedirectLookupError:
142+
return "RedirectLookupError";
143+
case NSchemeCache::TSchemeCacheNavigate::EStatus::AccessDenied:
144+
return "AccessDenied";
145+
default:
146+
return ::ToString(static_cast<int>(ev->Request->ResultSet.begin()->Status));
147+
}
148+
}
149+
116150
void TViewerPipeClient::RequestHiveDomainStats(NNodeWhiteboard::TTabletId hiveId) {
117151
TActorId pipeClient = ConnectTabletPipe(hiveId);
118152
THolder<TEvHive::TEvRequestHiveDomainStats> request = MakeHolder<TEvHive::TEvRequestHiveDomainStats>();
@@ -266,6 +300,11 @@ TViewerPipeClient::TRequestResponse<NConsole::TEvConsole::TEvGetNodeConfigRespon
266300
return MakeRequestToPipe<NConsole::TEvConsole::TEvGetNodeConfigResponse>(pipeClient, request.Release(), cookie);
267301
}
268302

303+
TViewerPipeClient::TRequestResponse<NConsole::TEvConsole::TEvGetAllConfigsResponse> TViewerPipeClient::MakeRequestConsoleGetAllConfigs() {
304+
TActorId pipeClient = ConnectTabletPipe(GetConsoleId());
305+
return MakeRequestToPipe<NConsole::TEvConsole::TEvGetAllConfigsResponse>(pipeClient, new NConsole::TEvConsole::TEvGetAllConfigsRequest());
306+
}
307+
269308
void TViewerPipeClient::RequestConsoleGetTenantStatus(const TString& path) {
270309
TActorId pipeClient = ConnectTabletPipe(GetConsoleId());
271310
THolder<NConsole::TEvConsole::TEvGetTenantStatusRequest> request = MakeHolder<NConsole::TEvConsole::TEvGetTenantStatusRequest>();

ydb/core/viewer/json_pipe_req.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ class TViewerPipeClient : public TActorBootstrapped<TViewerPipeClient> {
7373
TRequestResponse& operator =(TRequestResponse&&) = default;
7474

7575
void Set(std::unique_ptr<T>&& response) {
76+
constexpr bool hasErrorCheck = requires(const std::unique_ptr<T>& r) {TViewerPipeClient::IsSuccess(r);};
77+
if constexpr (hasErrorCheck) {
78+
if (!TViewerPipeClient::IsSuccess(response)) {
79+
Error(TViewerPipeClient::GetError(response));
80+
return;
81+
}
82+
}
7683
if (!IsDone()) {
7784
Span.EndOk();
7885
}
@@ -200,12 +207,17 @@ class TViewerPipeClient : public TActorBootstrapped<TViewerPipeClient> {
200207

201208
static TPathId GetPathId(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev);
202209
static TString GetPath(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev);
210+
211+
static bool IsSuccess(const std::unique_ptr<TEvTxProxySchemeCache::TEvNavigateKeySetResult>& ev);
212+
static TString GetError(const std::unique_ptr<TEvTxProxySchemeCache::TEvNavigateKeySetResult>& ev);
213+
203214
TRequestResponse<TEvHive::TEvResponseHiveDomainStats> MakeRequestHiveDomainStats(TTabletId hiveId);
204215
TRequestResponse<TEvHive::TEvResponseHiveStorageStats> MakeRequestHiveStorageStats(TTabletId hiveId);
205216
TRequestResponse<TEvHive::TEvResponseHiveNodeStats> MakeRequestHiveNodeStats(TTabletId hiveId, TEvHive::TEvRequestHiveNodeStats* request);
206217
void RequestConsoleListTenants();
207218
TRequestResponse<NConsole::TEvConsole::TEvListTenantsResponse> MakeRequestConsoleListTenants();
208219
TRequestResponse<NConsole::TEvConsole::TEvGetNodeConfigResponse> MakeRequestConsoleNodeConfigByTenant(TString tenant, ui64 cookie = 0);
220+
TRequestResponse<NConsole::TEvConsole::TEvGetAllConfigsResponse> MakeRequestConsoleGetAllConfigs();
209221
void RequestConsoleGetTenantStatus(const TString& path);
210222
TRequestResponse<NConsole::TEvConsole::TEvGetTenantStatusResponse> MakeRequestConsoleGetTenantStatus(const TString& path);
211223
void RequestBSControllerConfig();

ydb/core/viewer/storage_groups.h

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,15 +1112,7 @@ class TStorageGroups : public TViewerPipeClient {
11121112
return RequestDone();
11131113
}
11141114
auto& navigateResult(itNavigateKeySetResult->second);
1115-
if (ev->Get()->Request->ResultSet.size() == 1) {
1116-
if (ev->Get()->Request->ResultSet.begin()->Status == NSchemeCache::TSchemeCacheNavigate::EStatus::Ok) {
1117-
navigateResult.Set(std::move(ev));
1118-
} else {
1119-
navigateResult.Error(TStringBuilder() << "Error " << ev->Get()->Request->ResultSet.begin()->Status);
1120-
}
1121-
} else {
1122-
navigateResult.Error(TStringBuilder() << "Invalid number of results: " << ev->Get()->Request->ResultSet.size());
1123-
}
1115+
navigateResult.Set(std::move(ev));
11241116
if (navigateResult.IsOk()) {
11251117
TString path = CanonizePath(navigateResult->Request->ResultSet.begin()->Path);
11261118
TIntrusiveConstPtr<TSchemeCacheNavigate::TDomainDescription> domainDescription = navigateResult->Request->ResultSet.begin()->DomainDescription;

ydb/core/viewer/viewer_feature_flags.h

Lines changed: 112 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22
#include "json_pipe_req.h"
33
#include "viewer.h"
4+
#include <ydb/library/yaml_config/yaml_config.h>
45

56
namespace NKikimr::NViewer {
67

@@ -11,132 +12,174 @@ class TJsonFeatureFlags : public TViewerPipeClient {
1112
using TBase = TViewerPipeClient;
1213
TJsonSettings JsonSettings;
1314
ui32 Timeout = 0;
14-
1515
TString FilterDatabase;
1616
THashSet<TString> FilterFeatures;
17-
THashMap<ui64, TString> DatabaseByCookie;
18-
ui64 Cookie = 0;
19-
TString DomainPath;
20-
bool Direct = false;
21-
2217
TRequestResponse<NConsole::TEvConsole::TEvListTenantsResponse> TenantsResponse;
23-
THashMap<TString, TRequestResponse<NConsole::TEvConsole::TEvGetNodeConfigResponse>> NodeConfigResponses;
18+
TRequestResponse<NConsole::TEvConsole::TEvGetAllConfigsResponse> AllConfigsResponse;
19+
std::unordered_map<TString, TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>> PathNameNavigateKeySetResults;
20+
std::unordered_map<TPathId, TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>> PathIdNavigateKeySetResults;
2421

2522
public:
2623
TJsonFeatureFlags(IViewer* viewer, NMon::TEvHttpInfo::TPtr &ev)
2724
: TViewerPipeClient(viewer, ev)
2825
{}
2926

30-
void MakeNodeConfigRequest(const TString& database) {
31-
NodeConfigResponses[database] = MakeRequestConsoleNodeConfigByTenant(database, Cookie);
32-
DatabaseByCookie[Cookie++] = database;
33-
}
34-
3527
void Bootstrap() override {
3628
const auto& params(Event->Get()->Request.GetParams());
37-
3829
JsonSettings.EnumAsNumbers = !FromStringWithDefault<bool>(params.Get("enums"), true);
3930
JsonSettings.UI64AsString = !FromStringWithDefault<bool>(params.Get("ui64"), false);
4031
FilterDatabase = params.Get("database");
4132
StringSplitter(params.Get("features")).Split(',').SkipEmpty().Collect(&FilterFeatures);
42-
Direct = FromStringWithDefault<bool>(params.Get("direct"), Direct);
33+
bool direct = FromStringWithDefault<bool>(params.Get("direct"), false);
4334
Timeout = FromStringWithDefault<ui32>(params.Get("timeout"), 10000);
4435

45-
TIntrusivePtr<TDomainsInfo> domains = AppData()->DomainsInfo;
46-
auto* domain = domains->GetDomain();
47-
DomainPath = "/" + domain->Name;
36+
direct |= !TBase::Event->Get()->Request.GetHeader("X-Forwarded-From-Node").empty(); // we're already forwarding
37+
direct |= (FilterDatabase == AppData()->TenantName); // we're already on the right node
38+
if (FilterDatabase && !direct) {
39+
return RedirectToDatabase(FilterDatabase); // to find some dynamic node and redirect query there
40+
}
4841

49-
Direct |= !TBase::Event->Get()->Request.GetHeader("X-Forwarded-From-Node").empty(); // we're already forwarding
50-
Direct |= (FilterDatabase == AppData()->TenantName); // we're already on the right node
51-
if (FilterDatabase && !Direct) {
52-
RequestStateStorageEndpointsLookup(FilterDatabase); // to find some dynamic node and redirect there
53-
} else if (!FilterDatabase) {
54-
MakeNodeConfigRequest(DomainPath);
55-
TenantsResponse = MakeRequestConsoleListTenants();
42+
if (FilterDatabase) {
43+
PathNameNavigateKeySetResults[FilterDatabase] = MakeRequestSchemeCacheNavigate(FilterDatabase);
5644
} else {
57-
MakeNodeConfigRequest(FilterDatabase);
45+
TenantsResponse = MakeRequestConsoleListTenants();
5846
}
47+
AllConfigsResponse = MakeRequestConsoleGetAllConfigs();
5948

6049
Become(&TThis::StateWork, TDuration::MilliSeconds(Timeout), new TEvents::TEvWakeup());
6150
}
6251

63-
void HandleReply(TEvStateStorage::TEvBoardInfo::TPtr& ev) {
64-
TBase::ReplyAndPassAway(MakeForward(GetNodesFromBoardReply(ev)));
65-
}
66-
6752
STATEFN(StateWork) {
6853
switch (ev->GetTypeRewrite()) {
69-
hFunc(TEvStateStorage::TEvBoardInfo, HandleReply);
7054
hFunc(NConsole::TEvConsole::TEvListTenantsResponse, Handle);
71-
hFunc(NConsole::TEvConsole::TEvGetNodeConfigResponse, Handle);
55+
hFunc(NConsole::TEvConsole::TEvGetAllConfigsResponse, Handle);
56+
hFunc(TEvTxProxySchemeCache::TEvNavigateKeySetResult, Handle);
7257
hFunc(TEvTabletPipe::TEvClientConnected, TBase::Handle);
7358
cFunc(TEvents::TSystem::Wakeup, HandleTimeout);
7459
}
7560
}
7661

7762
void Handle(NConsole::TEvConsole::TEvListTenantsResponse::TPtr& ev) {
7863
TenantsResponse.Set(std::move(ev));
79-
Ydb::Cms::ListDatabasesResult listDatabasesResult;
80-
TenantsResponse->Record.GetResponse().operation().result().UnpackTo(&listDatabasesResult);
81-
for (const TString& path : listDatabasesResult.paths()) {
82-
MakeNodeConfigRequest(path);
64+
if (TenantsResponse.IsOk()) {
65+
Ydb::Cms::ListDatabasesResult listDatabasesResult;
66+
TenantsResponse->Record.GetResponse().operation().result().UnpackTo(&listDatabasesResult);
67+
for (const TString& database : listDatabasesResult.paths()) {
68+
if (PathNameNavigateKeySetResults.count(database) == 0) {
69+
PathNameNavigateKeySetResults[database] = MakeRequestSchemeCacheNavigate(database);
70+
}
71+
}
8372
}
8473
RequestDone();
8574
}
8675

87-
void Handle(NConsole::TEvConsole::TEvGetNodeConfigResponse::TPtr& ev) {
88-
TString database = DatabaseByCookie[ev.Get()->Cookie];
89-
NodeConfigResponses[database].Set(std::move(ev));
76+
void Handle(NConsole::TEvConsole::TEvGetAllConfigsResponse::TPtr& ev) {
77+
AllConfigsResponse.Set(std::move(ev));
9078
RequestDone();
9179
}
9280

93-
THashMap<TString, NKikimrViewer::TFeatureFlagsConfig::TFeatureFlag> ParseFeatureFlags(const NKikimrConfig::TFeatureFlags& featureFlags) {
94-
THashMap<TString, NKikimrViewer::TFeatureFlagsConfig::TFeatureFlag> features;
81+
void Handle(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
82+
TString path = GetPath(ev);
83+
if (path) {
84+
auto it = PathNameNavigateKeySetResults.find(path);
85+
if (it != PathNameNavigateKeySetResults.end() && !it->second.IsDone()) {
86+
it->second.Set(std::move(ev));
87+
if (it->second.IsOk()) {
88+
TSchemeCacheNavigate::TEntry& entry(it->second->Request->ResultSet.front());
89+
if (entry.DomainInfo) {
90+
if (entry.DomainInfo->ResourcesDomainKey && entry.DomainInfo->DomainKey != entry.DomainInfo->ResourcesDomainKey) {
91+
TPathId resourceDomainKey(entry.DomainInfo->ResourcesDomainKey);
92+
if (PathIdNavigateKeySetResults.count(resourceDomainKey) == 0) {
93+
PathIdNavigateKeySetResults[resourceDomainKey] = MakeRequestSchemeCacheNavigate(resourceDomainKey);
94+
}
95+
}
96+
}
97+
}
98+
RequestDone();
99+
return;
100+
}
101+
}
102+
TPathId pathId = GetPathId(ev);
103+
if (pathId) {
104+
auto it = PathIdNavigateKeySetResults.find(pathId);
105+
if (it != PathIdNavigateKeySetResults.end() && !it->second.IsDone()) {
106+
it->second.Set(std::move(ev));
107+
RequestDone();
108+
return;
109+
}
110+
}
111+
}
112+
113+
void ParseFeatureFlags(const NKikimrConfig::TFeatureFlags& featureFlags, NKikimrViewer::TFeatureFlagsConfig::TDatabase& result) {
95114
const google::protobuf::Reflection* reflection = featureFlags.GetReflection();
96115
const google::protobuf::Descriptor* descriptor = featureFlags.GetDescriptor();
97-
98116
for (int i = 0; i < descriptor->field_count(); ++i) {
99117
const google::protobuf::FieldDescriptor* field = descriptor->field(i);
100118
if (field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_BOOL) {
101-
auto& feat = features[field->name()];
102-
feat.SetName(field->name());
103-
if (reflection->HasField(featureFlags, field)) {
104-
feat.SetCurrent(reflection->GetBool(featureFlags, field));
105-
}
106-
if (field->has_default_value()) {
107-
feat.SetDefault(field->default_value_bool());
119+
if (FilterFeatures.empty() || FilterFeatures.count(field->name())) {
120+
auto flag = result.AddFeatureFlags();
121+
flag->SetName(field->name());
122+
if (reflection->HasField(featureFlags, field)) {
123+
flag->SetCurrent(reflection->GetBool(featureFlags, field));
124+
}
125+
if (field->has_default_value()) {
126+
flag->SetDefault(field->default_value_bool());
127+
}
108128
}
109129
}
110130
}
111-
return features;
112131
}
113132

114-
void ReplyAndPassAway() override {
115-
THashMap<TString, THashMap<TString, NKikimrViewer::TFeatureFlagsConfig::TFeatureFlag>> FeatureFlagsByDatabase;
116-
for (const auto& [database, response] : NodeConfigResponses) {
117-
NKikimrConsole::TGetNodeConfigResponse rec = response->Record;
118-
FeatureFlagsByDatabase[database] = ParseFeatureFlags(rec.GetConfig().GetFeatureFlags());
119-
}
120-
121-
auto domainFeaturesIt = FeatureFlagsByDatabase.find(DomainPath);
122-
if (domainFeaturesIt == FeatureFlagsByDatabase.end()) {
123-
return TBase::ReplyAndPassAway(GetHTTPINTERNALERROR("text/plain", "No domain info from Console"));
133+
void ParseConfig(const TString& database, NKikimrViewer::TFeatureFlagsConfig& result) {
134+
if (AllConfigsResponse.IsOk()) {
135+
TString realDatabase = database;
136+
NKikimrViewer::TFeatureFlagsConfig_TDatabase* databaseProto = nullptr;
137+
auto itNavigate = PathNameNavigateKeySetResults.find(database);
138+
if (itNavigate != PathNameNavigateKeySetResults.end() && itNavigate->second.IsOk()) {
139+
databaseProto = result.AddDatabases();
140+
databaseProto->SetName(database);
141+
TSchemeCacheNavigate::TEntry& entry(itNavigate->second->Request->ResultSet.front());
142+
if (entry.DomainInfo) {
143+
if (entry.DomainInfo->ResourcesDomainKey && entry.DomainInfo->DomainKey != entry.DomainInfo->ResourcesDomainKey) {
144+
TPathId resourceDomainKey(entry.DomainInfo->ResourcesDomainKey);
145+
auto it = PathIdNavigateKeySetResults.find(resourceDomainKey);
146+
if (it != PathIdNavigateKeySetResults.end() && it->second.IsOk() && it->second->Request->ResultSet.size() == 1) {
147+
realDatabase = CanonizePath(it->second->Request->ResultSet.begin()->Path);
148+
}
149+
}
150+
}
151+
} else {
152+
return;
153+
}
154+
NKikimrConfig::TAppConfig appConfig;
155+
if (AllConfigsResponse->Record.GetResponse().config()) {
156+
try {
157+
NYamlConfig::ResolveAndParseYamlConfig(AllConfigsResponse->Record.GetResponse().config(), {}, {{"tenant", realDatabase}}, appConfig);
158+
} catch (const std::exception& e) {
159+
BLOG_ERROR("Failed to parse config for tenant " << realDatabase << ": " << e.what());
160+
}
161+
ParseFeatureFlags(appConfig.GetFeatureFlags(), *databaseProto);
162+
} else {
163+
ParseFeatureFlags(AppData()->FeatureFlags, *databaseProto);
164+
}
124165
}
166+
}
125167

168+
void ReplyAndPassAway() override {
126169
// prepare response
127170
NKikimrViewer::TFeatureFlagsConfig Result;
128171
Result.SetVersion(Viewer->GetCapabilityVersion("/viewer/feature_flags"));
129-
for (const auto& [database, features] : FeatureFlagsByDatabase) {
130-
auto databaseProto = Result.AddDatabases();
131-
databaseProto->SetName(database);
132-
for (const auto& [name, featProto] : features) {
133-
if (FilterFeatures.empty() || FilterFeatures.find(name) != FilterFeatures.end()) {
134-
auto flag = databaseProto->AddFeatureFlags();
135-
flag->CopyFrom(featProto);
172+
if (FilterDatabase) {
173+
ParseConfig(FilterDatabase, Result);
174+
} else {
175+
if (TenantsResponse.IsOk()) {
176+
Ydb::Cms::ListDatabasesResult listDatabasesResult;
177+
TenantsResponse->Record.GetResponse().operation().result().UnpackTo(&listDatabasesResult);
178+
for (const TString& database : listDatabasesResult.paths()) {
179+
ParseConfig(database, Result);
136180
}
137181
}
138182
}
139-
140183
TStringStream json;
141184
TProtoToJson::ProtoToJson(json, Result, JsonSettings);
142185
TBase::ReplyAndPassAway(GetHTTPOKJSON(json.Str()));
@@ -147,12 +190,13 @@ class TJsonFeatureFlags : public TViewerPipeClient {
147190
.Method = "get",
148191
.Tag = "viewer",
149192
.Summary = "Feature flags",
150-
.Description = "Returns feature flags of each database"
193+
.Description = "Returns feature flags of a database"
151194
});
152195
yaml.AddParameter({
153196
.Name = "database",
154197
.Description = "database name",
155198
.Type = "string",
199+
.Required = true,
156200
});
157201
yaml.AddParameter({
158202
.Name = "features",
@@ -169,16 +213,6 @@ class TJsonFeatureFlags : public TViewerPipeClient {
169213
.Description = "timeout in ms",
170214
.Type = "integer",
171215
});
172-
yaml.AddParameter({
173-
.Name = "enums",
174-
.Description = "convert enums to strings",
175-
.Type = "boolean",
176-
});
177-
yaml.AddParameter({
178-
.Name = "ui64",
179-
.Description = "return ui64 as number",
180-
.Type = "boolean",
181-
});
182216
yaml.SetResponseSchema(TProtoToYaml::ProtoToYamlSchema<NKikimrViewer::TFeatureFlagsConfig>());
183217
return yaml;
184218
}

0 commit comments

Comments
 (0)