Skip to content

Commit e4a2449

Browse files
committed
another fix for feature flags
1 parent be39b5a commit e4a2449

File tree

6 files changed

+156
-89
lines changed

6 files changed

+156
-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: 102 additions & 79 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,163 @@ 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);
83-
}
8464
RequestDone();
8565
}
8666

87-
void Handle(NConsole::TEvConsole::TEvGetNodeConfigResponse::TPtr& ev) {
88-
TString database = DatabaseByCookie[ev.Get()->Cookie];
89-
NodeConfigResponses[database].Set(std::move(ev));
67+
void Handle(NConsole::TEvConsole::TEvGetAllConfigsResponse::TPtr& ev) {
68+
AllConfigsResponse.Set(std::move(ev));
9069
RequestDone();
9170
}
9271

93-
THashMap<TString, NKikimrViewer::TFeatureFlagsConfig::TFeatureFlag> ParseFeatureFlags(const NKikimrConfig::TFeatureFlags& featureFlags) {
94-
THashMap<TString, NKikimrViewer::TFeatureFlagsConfig::TFeatureFlag> features;
72+
void Handle(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
73+
TString path = GetPath(ev);
74+
if (path) {
75+
auto it = PathNameNavigateKeySetResults.find(path);
76+
if (it != PathNameNavigateKeySetResults.end() && !it->second.IsDone()) {
77+
it->second.Set(std::move(ev));
78+
if (it->second.IsOk()) {
79+
TSchemeCacheNavigate::TEntry& entry(it->second->Request->ResultSet.front());
80+
if (entry.DomainInfo) {
81+
if (entry.DomainInfo->ResourcesDomainKey && entry.DomainInfo->DomainKey != entry.DomainInfo->ResourcesDomainKey) {
82+
TPathId resourceDomainKey(entry.DomainInfo->ResourcesDomainKey);
83+
PathIdNavigateKeySetResults[resourceDomainKey] = MakeRequestSchemeCacheNavigate(resourceDomainKey);
84+
}
85+
}
86+
}
87+
RequestDone();
88+
return;
89+
}
90+
}
91+
TPathId pathId = GetPathId(ev);
92+
if (pathId) {
93+
auto it = PathIdNavigateKeySetResults.find(pathId);
94+
if (it != PathIdNavigateKeySetResults.end() && !it->second.IsDone()) {
95+
it->second.Set(std::move(ev));
96+
RequestDone();
97+
return;
98+
}
99+
}
100+
}
101+
102+
void ParseFeatureFlags(const NKikimrConfig::TFeatureFlags& featureFlags, NKikimrViewer::TFeatureFlagsConfig::TDatabase& result) {
95103
const google::protobuf::Reflection* reflection = featureFlags.GetReflection();
96104
const google::protobuf::Descriptor* descriptor = featureFlags.GetDescriptor();
97-
98105
for (int i = 0; i < descriptor->field_count(); ++i) {
99106
const google::protobuf::FieldDescriptor* field = descriptor->field(i);
100107
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());
108+
if (FilterFeatures.empty() || FilterFeatures.count(field->name())) {
109+
auto flag = result.AddFeatureFlags();
110+
flag->SetName(field->name());
111+
if (reflection->HasField(featureFlags, field)) {
112+
flag->SetCurrent(reflection->GetBool(featureFlags, field));
113+
}
114+
if (field->has_default_value()) {
115+
flag->SetDefault(field->default_value_bool());
116+
}
108117
}
109118
}
110119
}
111-
return features;
112120
}
113121

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"));
122+
void ParseConfig(const TString& database, NKikimrViewer::TFeatureFlagsConfig& result) {
123+
if (AllConfigsResponse.IsOk()) {
124+
TString realDatabase = database;
125+
NKikimrViewer::TFeatureFlagsConfig_TDatabase* databaseProto = nullptr;
126+
auto itNavigate = PathNameNavigateKeySetResults.find(database);
127+
if (itNavigate != PathNameNavigateKeySetResults.end() && itNavigate->second.IsOk()) {
128+
databaseProto = result.AddDatabases();
129+
databaseProto->SetName(database);
130+
TSchemeCacheNavigate::TEntry& entry(itNavigate->second->Request->ResultSet.front());
131+
if (entry.DomainInfo) {
132+
if (entry.DomainInfo->ResourcesDomainKey && entry.DomainInfo->DomainKey != entry.DomainInfo->ResourcesDomainKey) {
133+
TPathId resourceDomainKey(entry.DomainInfo->ResourcesDomainKey);
134+
auto it = PathIdNavigateKeySetResults.find(resourceDomainKey);
135+
if (it != PathIdNavigateKeySetResults.end() && it->second.IsOk() && it->second->Request->ResultSet.size() == 1) {
136+
realDatabase = CanonizePath(it->second->Request->ResultSet.begin()->Path);
137+
}
138+
}
139+
}
140+
} else {
141+
return;
142+
}
143+
NKikimrConfig::TAppConfig appConfig;
144+
if (AllConfigsResponse->Record.GetResponse().config()) {
145+
try {
146+
NYamlConfig::ResolveAndParseYamlConfig(AllConfigsResponse->Record.GetResponse().config(), {}, {{"tenant", realDatabase}}, appConfig);
147+
} catch (const std::exception& e) {
148+
BLOG_ERROR("Failed to parse config for tenant " << realDatabase << ": " << e.what());
149+
}
150+
ParseFeatureFlags(appConfig.GetFeatureFlags(), *databaseProto);
151+
} else {
152+
ParseFeatureFlags(AppData()->FeatureFlags, *databaseProto);
153+
}
124154
}
155+
}
125156

157+
void ReplyAndPassAway() override {
126158
// prepare response
127159
NKikimrViewer::TFeatureFlagsConfig Result;
128160
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);
161+
if (FilterDatabase) {
162+
ParseConfig(FilterDatabase, Result);
163+
} else {
164+
if (TenantsResponse.IsOk()) {
165+
Ydb::Cms::ListDatabasesResult listDatabasesResult;
166+
TenantsResponse->Record.GetResponse().operation().result().UnpackTo(&listDatabasesResult);
167+
for (const TString& database : listDatabasesResult.paths()) {
168+
ParseConfig(database, Result);
136169
}
137170
}
138171
}
139-
140172
TStringStream json;
141173
TProtoToJson::ProtoToJson(json, Result, JsonSettings);
142174
TBase::ReplyAndPassAway(GetHTTPOKJSON(json.Str()));
@@ -147,12 +179,13 @@ class TJsonFeatureFlags : public TViewerPipeClient {
147179
.Method = "get",
148180
.Tag = "viewer",
149181
.Summary = "Feature flags",
150-
.Description = "Returns feature flags of each database"
182+
.Description = "Returns feature flags of a database"
151183
});
152184
yaml.AddParameter({
153185
.Name = "database",
154186
.Description = "database name",
155187
.Type = "string",
188+
.Required = true,
156189
});
157190
yaml.AddParameter({
158191
.Name = "features",
@@ -169,16 +202,6 @@ class TJsonFeatureFlags : public TViewerPipeClient {
169202
.Description = "timeout in ms",
170203
.Type = "integer",
171204
});
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-
});
182205
yaml.SetResponseSchema(TProtoToYaml::ProtoToYamlSchema<NKikimrViewer::TFeatureFlagsConfig>());
183206
return yaml;
184207
}

ydb/core/viewer/viewer_tenantinfo.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ class TJsonTenantInfo : public TViewerPipeClient {
379379
TString path = GetPath(ev);
380380
auto& result(NavigateKeySetResult[path]);
381381
result.Set(std::move(ev));
382-
if (result.Get()->Request->ResultSet.size() == 1 && result.Get()->Request->ResultSet.begin()->Status == NSchemeCache::TSchemeCacheNavigate::EStatus::Ok) {
382+
if (result.IsOk()) {
383383
auto domainInfo = result.Get()->Request->ResultSet.begin()->DomainInfo;
384384
TTabletId hiveId = domainInfo->Params.GetHive();
385385
if (hiveId) {

ydb/core/viewer/ya.make

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ PEERDIR(
571571
ydb/core/viewer/yaml
572572
ydb/core/viewer/protos
573573
ydb/library/persqueue/topic_parser
574+
ydb/library/yaml_config
574575
ydb/public/api/protos
575576
ydb/public/lib/deprecated/kicli
576577
ydb/public/lib/json_value

0 commit comments

Comments
 (0)