Skip to content

Commit 5446d85

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

File tree

7 files changed

+171
-91
lines changed

7 files changed

+171
-91
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: 115 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,176 @@ 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-
17+
bool ChangedOnly = false;
2218
TRequestResponse<NConsole::TEvConsole::TEvListTenantsResponse> TenantsResponse;
23-
THashMap<TString, TRequestResponse<NConsole::TEvConsole::TEvGetNodeConfigResponse>> NodeConfigResponses;
19+
TRequestResponse<NConsole::TEvConsole::TEvGetAllConfigsResponse> AllConfigsResponse;
20+
std::unordered_map<TString, TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>> PathNameNavigateKeySetResults;
21+
std::unordered_map<TPathId, TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>> PathIdNavigateKeySetResults;
2422

2523
public:
2624
TJsonFeatureFlags(IViewer* viewer, NMon::TEvHttpInfo::TPtr &ev)
2725
: TViewerPipeClient(viewer, ev)
2826
{}
2927

30-
void MakeNodeConfigRequest(const TString& database) {
31-
NodeConfigResponses[database] = MakeRequestConsoleNodeConfigByTenant(database, Cookie);
32-
DatabaseByCookie[Cookie++] = database;
33-
}
34-
3528
void Bootstrap() override {
3629
const auto& params(Event->Get()->Request.GetParams());
37-
3830
JsonSettings.EnumAsNumbers = !FromStringWithDefault<bool>(params.Get("enums"), true);
3931
JsonSettings.UI64AsString = !FromStringWithDefault<bool>(params.Get("ui64"), false);
4032
FilterDatabase = params.Get("database");
4133
StringSplitter(params.Get("features")).Split(',').SkipEmpty().Collect(&FilterFeatures);
42-
Direct = FromStringWithDefault<bool>(params.Get("direct"), Direct);
34+
bool direct = FromStringWithDefault<bool>(params.Get("direct"), false);
4335
Timeout = FromStringWithDefault<ui32>(params.Get("timeout"), 10000);
36+
ChangedOnly = FromStringWithDefault<bool>(params.Get("changed"), ChangedOnly);
4437

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

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();
44+
if (FilterDatabase) {
45+
PathNameNavigateKeySetResults[FilterDatabase] = MakeRequestSchemeCacheNavigate(FilterDatabase);
5646
} else {
57-
MakeNodeConfigRequest(FilterDatabase);
47+
TenantsResponse = MakeRequestConsoleListTenants();
5848
}
49+
AllConfigsResponse = MakeRequestConsoleGetAllConfigs();
5950

6051
Become(&TThis::StateWork, TDuration::MilliSeconds(Timeout), new TEvents::TEvWakeup());
6152
}
6253

63-
void HandleReply(TEvStateStorage::TEvBoardInfo::TPtr& ev) {
64-
TBase::ReplyAndPassAway(MakeForward(GetNodesFromBoardReply(ev)));
65-
}
66-
6754
STATEFN(StateWork) {
6855
switch (ev->GetTypeRewrite()) {
69-
hFunc(TEvStateStorage::TEvBoardInfo, HandleReply);
7056
hFunc(NConsole::TEvConsole::TEvListTenantsResponse, Handle);
71-
hFunc(NConsole::TEvConsole::TEvGetNodeConfigResponse, Handle);
57+
hFunc(NConsole::TEvConsole::TEvGetAllConfigsResponse, Handle);
58+
hFunc(TEvTxProxySchemeCache::TEvNavigateKeySetResult, Handle);
7259
hFunc(TEvTabletPipe::TEvClientConnected, TBase::Handle);
7360
cFunc(TEvents::TSystem::Wakeup, HandleTimeout);
7461
}
7562
}
7663

7764
void Handle(NConsole::TEvConsole::TEvListTenantsResponse::TPtr& ev) {
7865
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);
66+
if (TenantsResponse.IsOk()) {
67+
Ydb::Cms::ListDatabasesResult listDatabasesResult;
68+
TenantsResponse->Record.GetResponse().operation().result().UnpackTo(&listDatabasesResult);
69+
for (const TString& database : listDatabasesResult.paths()) {
70+
if (PathNameNavigateKeySetResults.count(database) == 0) {
71+
PathNameNavigateKeySetResults[database] = MakeRequestSchemeCacheNavigate(database);
72+
}
73+
}
74+
}
75+
if (PathNameNavigateKeySetResults.empty()) {
76+
if (AppData()->DomainsInfo && AppData()->DomainsInfo->Domain) {
77+
TString domain = "/" + AppData()->DomainsInfo->Domain->Name;
78+
PathNameNavigateKeySetResults[domain] = MakeRequestSchemeCacheNavigate(domain);
79+
}
8380
}
8481
RequestDone();
8582
}
8683

87-
void Handle(NConsole::TEvConsole::TEvGetNodeConfigResponse::TPtr& ev) {
88-
TString database = DatabaseByCookie[ev.Get()->Cookie];
89-
NodeConfigResponses[database].Set(std::move(ev));
84+
void Handle(NConsole::TEvConsole::TEvGetAllConfigsResponse::TPtr& ev) {
85+
AllConfigsResponse.Set(std::move(ev));
9086
RequestDone();
9187
}
9288

93-
THashMap<TString, NKikimrViewer::TFeatureFlagsConfig::TFeatureFlag> ParseFeatureFlags(const NKikimrConfig::TFeatureFlags& featureFlags) {
94-
THashMap<TString, NKikimrViewer::TFeatureFlagsConfig::TFeatureFlag> features;
89+
void Handle(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
90+
TString path = GetPath(ev);
91+
if (path) {
92+
auto it = PathNameNavigateKeySetResults.find(path);
93+
if (it != PathNameNavigateKeySetResults.end() && !it->second.IsDone()) {
94+
it->second.Set(std::move(ev));
95+
if (it->second.IsOk()) {
96+
TSchemeCacheNavigate::TEntry& entry(it->second->Request->ResultSet.front());
97+
if (entry.DomainInfo) {
98+
if (entry.DomainInfo->ResourcesDomainKey && entry.DomainInfo->DomainKey != entry.DomainInfo->ResourcesDomainKey) {
99+
TPathId resourceDomainKey(entry.DomainInfo->ResourcesDomainKey);
100+
if (PathIdNavigateKeySetResults.count(resourceDomainKey) == 0) {
101+
PathIdNavigateKeySetResults[resourceDomainKey] = MakeRequestSchemeCacheNavigate(resourceDomainKey);
102+
}
103+
}
104+
}
105+
}
106+
RequestDone();
107+
return;
108+
}
109+
}
110+
TPathId pathId = GetPathId(ev);
111+
if (pathId) {
112+
auto it = PathIdNavigateKeySetResults.find(pathId);
113+
if (it != PathIdNavigateKeySetResults.end() && !it->second.IsDone()) {
114+
it->second.Set(std::move(ev));
115+
RequestDone();
116+
return;
117+
}
118+
}
119+
}
120+
121+
void ParseFeatureFlags(const NKikimrConfig::TFeatureFlags& featureFlags, NKikimrViewer::TFeatureFlagsConfig::TDatabase& result) {
95122
const google::protobuf::Reflection* reflection = featureFlags.GetReflection();
96123
const google::protobuf::Descriptor* descriptor = featureFlags.GetDescriptor();
97-
98124
for (int i = 0; i < descriptor->field_count(); ++i) {
99125
const google::protobuf::FieldDescriptor* field = descriptor->field(i);
100126
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());
127+
if (FilterFeatures.empty() || FilterFeatures.count(field->name())) {
128+
bool hasField = reflection->HasField(featureFlags, field);
129+
if (ChangedOnly && !hasField) {
130+
continue;
131+
}
132+
auto flag = result.AddFeatureFlags();
133+
flag->SetName(field->name());
134+
if (hasField) {
135+
flag->SetCurrent(reflection->GetBool(featureFlags, field));
136+
}
137+
if (field->has_default_value()) {
138+
flag->SetDefault(field->default_value_bool());
139+
}
108140
}
109141
}
110142
}
111-
return features;
112143
}
113144

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"));
145+
void ParseConfig(const TString& database,
146+
const TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>& navigate,
147+
NKikimrViewer::TFeatureFlagsConfig& result) {
148+
if (AllConfigsResponse.IsOk()) {
149+
TString realDatabase = database;
150+
auto databaseProto = result.AddDatabases();
151+
databaseProto->SetName(database);
152+
TSchemeCacheNavigate::TEntry& entry(navigate->Request->ResultSet.front());
153+
if (entry.DomainInfo) {
154+
if (entry.DomainInfo->ResourcesDomainKey && entry.DomainInfo->DomainKey != entry.DomainInfo->ResourcesDomainKey) {
155+
TPathId resourceDomainKey(entry.DomainInfo->ResourcesDomainKey);
156+
auto it = PathIdNavigateKeySetResults.find(resourceDomainKey);
157+
if (it != PathIdNavigateKeySetResults.end() && it->second.IsOk() && it->second->Request->ResultSet.size() == 1) {
158+
realDatabase = CanonizePath(it->second->Request->ResultSet.begin()->Path);
159+
}
160+
}
161+
}
162+
NKikimrConfig::TAppConfig appConfig;
163+
if (AllConfigsResponse->Record.GetResponse().config()) {
164+
try {
165+
NYamlConfig::ResolveAndParseYamlConfig(AllConfigsResponse->Record.GetResponse().config(), {}, {{"tenant", realDatabase}}, appConfig);
166+
} catch (const std::exception& e) {
167+
BLOG_ERROR("Failed to parse config for tenant " << realDatabase << ": " << e.what());
168+
}
169+
ParseFeatureFlags(appConfig.GetFeatureFlags(), *databaseProto);
170+
} else {
171+
ParseFeatureFlags(AppData()->FeatureFlags, *databaseProto);
172+
}
124173
}
174+
}
125175

176+
void ReplyAndPassAway() override {
126177
// prepare response
127178
NKikimrViewer::TFeatureFlagsConfig Result;
128179
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);
136-
}
180+
for (const auto& [database, navigate] : PathNameNavigateKeySetResults) {
181+
if (navigate.IsOk()) {
182+
ParseConfig(database, navigate, Result);
137183
}
138184
}
139-
140185
TStringStream json;
141186
TProtoToJson::ProtoToJson(json, Result, JsonSettings);
142187
TBase::ReplyAndPassAway(GetHTTPOKJSON(json.Str()));
@@ -147,12 +192,13 @@ class TJsonFeatureFlags : public TViewerPipeClient {
147192
.Method = "get",
148193
.Tag = "viewer",
149194
.Summary = "Feature flags",
150-
.Description = "Returns feature flags of each database"
195+
.Description = "Returns feature flags of a database"
151196
});
152197
yaml.AddParameter({
153198
.Name = "database",
154199
.Description = "database name",
155200
.Type = "string",
201+
.Required = true,
156202
});
157203
yaml.AddParameter({
158204
.Name = "features",
@@ -169,16 +215,6 @@ class TJsonFeatureFlags : public TViewerPipeClient {
169215
.Description = "timeout in ms",
170216
.Type = "integer",
171217
});
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-
});
182218
yaml.SetResponseSchema(TProtoToYaml::ProtoToYamlSchema<NKikimrViewer::TFeatureFlagsConfig>());
183219
return yaml;
184220
}

0 commit comments

Comments
 (0)