1
1
#pragma once
2
2
#include " json_pipe_req.h"
3
3
#include " viewer.h"
4
+ #include < ydb/library/yaml_config/yaml_config.h>
4
5
5
6
namespace NKikimr ::NViewer {
6
7
@@ -11,127 +12,176 @@ class TJsonFeatureFlags : public TViewerPipeClient {
11
12
using TBase = TViewerPipeClient;
12
13
TJsonSettings JsonSettings;
13
14
ui32 Timeout = 0 ;
14
-
15
15
TString FilterDatabase;
16
16
THashSet<TString> FilterFeatures;
17
- THashMap<ui64, TString> DatabaseByCookie;
18
- ui64 Cookie = 0 ;
19
- TString DomainPath;
20
- bool Direct = false ;
21
-
17
+ bool ChangedOnly = false ;
22
18
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;
24
22
25
23
public:
26
24
TJsonFeatureFlags (IViewer* viewer, NMon::TEvHttpInfo::TPtr &ev)
27
25
: TViewerPipeClient(viewer, ev)
28
26
{}
29
27
30
- void MakeNodeConfigRequest (const TString& database) {
31
- NodeConfigResponses[database] = MakeRequestConsoleNodeConfigByTenant (database, Cookie);
32
- DatabaseByCookie[Cookie++] = database;
33
- }
34
-
35
28
void Bootstrap () override {
36
29
const auto & params (Event->Get ()->Request .GetParams ());
37
-
38
30
JsonSettings.EnumAsNumbers = !FromStringWithDefault<bool >(params.Get (" enums" ), true );
39
31
JsonSettings.UI64AsString = !FromStringWithDefault<bool >(params.Get (" ui64" ), false );
40
32
FilterDatabase = params.Get (" database" );
41
33
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 );
43
35
Timeout = FromStringWithDefault<ui32>(params.Get (" timeout" ), 10000 );
36
+ ChangedOnly = FromStringWithDefault<bool >(params.Get (" changed" ), ChangedOnly);
44
37
45
- TIntrusivePtr<TDomainsInfo> domains = AppData ()->DomainsInfo ;
46
- auto * domain = domains->GetDomain ();
47
- DomainPath = " /" + domain->Name ;
48
-
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) {
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) {
52
41
return RedirectToDatabase (FilterDatabase); // to find some dynamic node and redirect query there
53
- } else if (!FilterDatabase) {
54
- MakeNodeConfigRequest (DomainPath);
55
- TenantsResponse = MakeRequestConsoleListTenants ();
42
+ }
43
+
44
+ if (FilterDatabase) {
45
+ PathNameNavigateKeySetResults[FilterDatabase] = MakeRequestSchemeCacheNavigate (FilterDatabase);
56
46
} else {
57
- MakeNodeConfigRequest (FilterDatabase );
47
+ TenantsResponse = MakeRequestConsoleListTenants ( );
58
48
}
49
+ AllConfigsResponse = MakeRequestConsoleGetAllConfigs ();
59
50
60
51
Become (&TThis::StateWork, TDuration::MilliSeconds (Timeout), new TEvents::TEvWakeup ());
61
52
}
62
53
63
54
STATEFN (StateWork) {
64
55
switch (ev->GetTypeRewrite ()) {
65
56
hFunc (NConsole::TEvConsole::TEvListTenantsResponse, Handle);
66
- hFunc (NConsole::TEvConsole::TEvGetNodeConfigResponse, Handle);
57
+ hFunc (NConsole::TEvConsole::TEvGetAllConfigsResponse, Handle);
58
+ hFunc (TEvTxProxySchemeCache::TEvNavigateKeySetResult, Handle);
67
59
hFunc (TEvTabletPipe::TEvClientConnected, TBase::Handle);
68
60
cFunc (TEvents::TSystem::Wakeup, HandleTimeout);
69
61
}
70
62
}
71
63
72
64
void Handle (NConsole::TEvConsole::TEvListTenantsResponse::TPtr& ev) {
73
65
TenantsResponse.Set (std::move (ev));
74
- Ydb::Cms::ListDatabasesResult listDatabasesResult;
75
- TenantsResponse->Record .GetResponse ().operation ().result ().UnpackTo (&listDatabasesResult);
76
- for (const TString& path : listDatabasesResult.paths ()) {
77
- 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
+ }
78
80
}
79
81
RequestDone ();
80
82
}
81
83
82
- void Handle (NConsole::TEvConsole::TEvGetNodeConfigResponse::TPtr& ev) {
83
- TString database = DatabaseByCookie[ev.Get ()->Cookie ];
84
- NodeConfigResponses[database].Set (std::move (ev));
84
+ void Handle (NConsole::TEvConsole::TEvGetAllConfigsResponse::TPtr& ev) {
85
+ AllConfigsResponse.Set (std::move (ev));
85
86
RequestDone ();
86
87
}
87
88
88
- THashMap<TString, NKikimrViewer::TFeatureFlagsConfig::TFeatureFlag> ParseFeatureFlags (const NKikimrConfig::TFeatureFlags& featureFlags) {
89
- 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) {
90
122
const google::protobuf::Reflection* reflection = featureFlags.GetReflection ();
91
123
const google::protobuf::Descriptor* descriptor = featureFlags.GetDescriptor ();
92
-
93
124
for (int i = 0 ; i < descriptor->field_count (); ++i) {
94
125
const google::protobuf::FieldDescriptor* field = descriptor->field (i);
95
126
if (field->cpp_type () == google::protobuf::FieldDescriptor::CPPTYPE_BOOL) {
96
- auto & feat = features[field->name ()];
97
- feat.SetName (field->name ());
98
- if (reflection->HasField (featureFlags, field)) {
99
- feat.SetCurrent (reflection->GetBool (featureFlags, field));
100
- }
101
- if (field->has_default_value ()) {
102
- 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
+ }
103
140
}
104
141
}
105
142
}
106
- return features;
107
143
}
108
144
109
- void ReplyAndPassAway () override {
110
- THashMap<TString, THashMap<TString, NKikimrViewer::TFeatureFlagsConfig::TFeatureFlag>> FeatureFlagsByDatabase;
111
- for (const auto & [database, response] : NodeConfigResponses) {
112
- NKikimrConsole::TGetNodeConfigResponse rec = response->Record ;
113
- FeatureFlagsByDatabase[database] = ParseFeatureFlags (rec.GetConfig ().GetFeatureFlags ());
114
- }
115
-
116
- auto domainFeaturesIt = FeatureFlagsByDatabase.find (DomainPath);
117
- if (domainFeaturesIt == FeatureFlagsByDatabase.end ()) {
118
- 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
+ }
119
173
}
174
+ }
120
175
176
+ void ReplyAndPassAway () override {
121
177
// prepare response
122
178
NKikimrViewer::TFeatureFlagsConfig Result;
123
179
Result.SetVersion (Viewer->GetCapabilityVersion (" /viewer/feature_flags" ));
124
- for (const auto & [database, features] : FeatureFlagsByDatabase) {
125
- auto databaseProto = Result.AddDatabases ();
126
- databaseProto->SetName (database);
127
- for (const auto & [name, featProto] : features) {
128
- if (FilterFeatures.empty () || FilterFeatures.find (name) != FilterFeatures.end ()) {
129
- auto flag = databaseProto->AddFeatureFlags ();
130
- flag->CopyFrom (featProto);
131
- }
180
+ for (const auto & [database, navigate] : PathNameNavigateKeySetResults) {
181
+ if (navigate.IsOk ()) {
182
+ ParseConfig (database, navigate, Result);
132
183
}
133
184
}
134
-
135
185
TStringStream json;
136
186
TProtoToJson::ProtoToJson (json, Result, JsonSettings);
137
187
TBase::ReplyAndPassAway (GetHTTPOKJSON (json.Str ()));
@@ -142,12 +192,13 @@ class TJsonFeatureFlags : public TViewerPipeClient {
142
192
.Method = " get" ,
143
193
.Tag = " viewer" ,
144
194
.Summary = " Feature flags" ,
145
- .Description = " Returns feature flags of each database"
195
+ .Description = " Returns feature flags of a database"
146
196
});
147
197
yaml.AddParameter ({
148
198
.Name = " database" ,
149
199
.Description = " database name" ,
150
200
.Type = " string" ,
201
+ .Required = true ,
151
202
});
152
203
yaml.AddParameter ({
153
204
.Name = " features" ,
@@ -164,16 +215,6 @@ class TJsonFeatureFlags : public TViewerPipeClient {
164
215
.Description = " timeout in ms" ,
165
216
.Type = " integer" ,
166
217
});
167
- yaml.AddParameter ({
168
- .Name = " enums" ,
169
- .Description = " convert enums to strings" ,
170
- .Type = " boolean" ,
171
- });
172
- yaml.AddParameter ({
173
- .Name = " ui64" ,
174
- .Description = " return ui64 as number" ,
175
- .Type = " boolean" ,
176
- });
177
218
yaml.SetResponseSchema (TProtoToYaml::ProtoToYamlSchema<NKikimrViewer::TFeatureFlagsConfig>());
178
219
return yaml;
179
220
}
0 commit comments