Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 155 additions & 8 deletions ydb/core/cms/console/configs_dispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,44 @@ class TConfigsDispatcher : public TActorBootstrapped<TConfigsDispatcher> {
void Handle(TEvConsole::TEvGetNodeLabelsRequest::TPtr &ev);
void Handle(TEvConsole::TEvFetchStartupConfigRequest::TPtr &ev);
void Handle(TEvConsole::TEvGetNodeConfigurationVersionRequest::TPtr &ev);
void Handle(TEvConfigsDispatcher::TEvGetStateRequest::TPtr &ev);
void Handle(TEvConfigsDispatcher::TEvGetStorageYamlRequest::TPtr &ev);

void ReplyMonJson(TActorId mailbox);

EConfigSource DetermineConfigSource() const {
if (auto it = Labels.find("config_source"); it != Labels.end()) {
if (it->second == "seed_nodes") {
return EConfigSource::SeedNodes;
}
}
return EConfigSource::DynamicConfig;
}

TConfigsDispatcherState GetState() const {
TConfigsDispatcherState state;

auto configSource = DetermineConfigSource();
state.ConfigSource = configSource;

if (auto it = Labels.find("config_source"); it != Labels.end()) {
state.ConfigSourceLabel = it->second;
}

if (auto it = Labels.find("configuration_version"); it != Labels.end()) {
state.ConfigurationVersion = it->second;
}

state.HasStorageYaml = !StartupStorageYaml.empty();
state.StorageYamlSize = StartupStorageYaml.size();
state.YamlConfigEnabled = YamlConfigEnabled;
state.SubscriptionsCount = SubscriptionsByKinds.size();
state.LastReplayUsedSeedNodesPath = LastReplayUsedSeedNodesPath;
state.LastReplayUsedDynamicConfigPath = LastReplayUsedDynamicConfigPath;
state.Labels = Labels;

return state;
}

STATEFN(StateInit)
{
Expand All @@ -203,6 +239,8 @@ class TConfigsDispatcher : public TActorBootstrapped<TConfigsDispatcher> {
hFuncTraced(TEvConfigsDispatcher::TEvGetConfigRequest, Handle);
hFuncTraced(TEvConfigsDispatcher::TEvSetConfigSubscriptionRequest, Handle);
hFuncTraced(TEvConfigsDispatcher::TEvRemoveConfigSubscriptionRequest, Handle);
hFuncTraced(TEvConfigsDispatcher::TEvGetStateRequest, Handle);
hFuncTraced(TEvConfigsDispatcher::TEvGetStorageYamlRequest, Handle);
// Resolve
hFunc(TEvConsole::TEvGetNodeLabelsRequest, Handle);
hFunc(TEvConsole::TEvFetchStartupConfigRequest, Handle);
Expand All @@ -228,6 +266,8 @@ class TConfigsDispatcher : public TActorBootstrapped<TConfigsDispatcher> {
hFuncTraced(TEvConfigsDispatcher::TEvRemoveConfigSubscriptionRequest, Handle);
hFuncTraced(TEvConsole::TEvConfigNotificationResponse, Handle);
IgnoreFunc(TEvConfigsDispatcher::TEvSetConfigSubscriptionResponse);
hFuncTraced(TEvConfigsDispatcher::TEvGetStateRequest, Handle);
hFuncTraced(TEvConfigsDispatcher::TEvGetStorageYamlRequest, Handle);
// Resolve
hFunc(TEvConsole::TEvGetNodeLabelsRequest, Handle);
hFunc(TEvConsole::TEvFetchStartupConfigRequest, Handle);
Expand All @@ -249,6 +289,7 @@ class TConfigsDispatcher : public TActorBootstrapped<TConfigsDispatcher> {
const NKikimrConfig::TAppConfig BaseConfig;
NKikimrConfig::TAppConfig CurrentConfig;
const TString StartupConfigYaml;
const TString StartupStorageYaml;
NKikimrConfig::TAppConfig CandidateStartupConfig;
bool StartupConfigProcessError = false;
bool StartupConfigProcessDiff = false;
Expand All @@ -263,6 +304,10 @@ class TConfigsDispatcher : public TActorBootstrapped<TConfigsDispatcher> {
TVector<TActorId> HttpRequests;
TActorId CommonSubscriptionClient;
TDeque<TAutoPtr<IEventHandle>> EventsQueue;

// Observability: Track which replay path was used
bool LastReplayUsedSeedNodesPath = false;
bool LastReplayUsedDynamicConfigPath = false;

THashMap<TActorId, TSubscription::TPtr> SubscriptionsBySubscriber;
THashMap<TDynBitMap, TSubscription::TPtr> SubscriptionsByKinds;
Expand All @@ -285,6 +330,7 @@ TConfigsDispatcher::TConfigsDispatcher(const TConfigsDispatcherInitInfo& initInf
, BaseConfig(initInfo.InitialConfig)
, CurrentConfig(initInfo.InitialConfig)
, StartupConfigYaml(initInfo.StartupConfigYaml)
, StartupStorageYaml(initInfo.StartupStorageYaml)
, CandidateStartupConfig(initInfo.InitialConfig)
, DebugInfo(initInfo.DebugInfo)
, RecordedInitialConfiguratorDeps(std::move(initInfo.RecordedInitialConfiguratorDeps))
Expand Down Expand Up @@ -442,6 +488,20 @@ void TConfigsDispatcher::ReplyMonJson(TActorId mailbox) {
response.InsertValue("yaml_config", MainYamlConfig);
response.InsertValue("resolved_json_config", NJson::ReadJsonFastTree(ResolvedJsonConfig, true));
response.InsertValue("current_json_config", NJson::ReadJsonFastTree(NProtobufJson::Proto2Json(CurrentConfig, NYamlConfig::GetProto2JsonConfig()), true));

auto state = GetState();
if (auto it = Labels.find("config_source"); it != Labels.end()) {
response.InsertValue("config_source", it->second);
}
if (auto it = Labels.find("configuration_version"); it != Labels.end()) {
response.InsertValue("configuration_version", it->second);
}
response.InsertValue("has_storage_yaml", state.HasStorageYaml);
if (state.HasStorageYaml) {
response.InsertValue("storage_yaml_size", static_cast<i64>(state.StorageYamlSize));
}
response.InsertValue("last_replay_seed_nodes", state.LastReplayUsedSeedNodesPath);
response.InsertValue("last_replay_dynamic_config", state.LastReplayUsedDynamicConfigPath);

if (DebugInfo) {
// TODO: write custom json serializer for security fields
Expand Down Expand Up @@ -652,6 +712,19 @@ void TConfigsDispatcher::Handle(TEvInterconnect::TEvNodesInfo::TPtr &ev)
: s == &TThis::StateInit ? "StateInit"
: "Unknown" ) << Endl;
str << "YamlConfigEnabled: " << YamlConfigEnabled << Endl;

str << Endl << "=== Configuration Source ===" << Endl;
auto state = GetState();
str << state.ToDebugString() << Endl;
if (LastReplayUsedSeedNodesPath) {
str << "Last Replay Path: Seed Nodes (ConfigClient)" << Endl;
} else if (LastReplayUsedDynamicConfigPath) {
str << "Last Replay Path: Dynamic Config (DynConfigClient)" << Endl;
} else {
str << "Last Replay Path: Not yet executed" << Endl;
}
str << Endl;

str << "Subscriptions: " << Endl;
for (auto &[kinds, subscription] : SubscriptionsByKinds) {
str << "- Kinds: " << KindsToString(kinds) << Endl
Expand Down Expand Up @@ -759,6 +832,34 @@ void TConfigsDispatcher::Handle(TEvInterconnect::TEvNodesInfo::TPtr &ev)
}
}
str << "<br />" << Endl;
COLLAPSED_REF_CONTENT("storage-yaml-config", "Storage YAML Config (Seed Nodes)") {
if (!StartupStorageYaml.empty()) {
DIV() {
TAG(TH5) {
str << "Startup Storage Config (from seed nodes)" << Endl;
}
TAG_CLASS_STYLE(TDiv, "configs-dispatcher", "padding: 0 12px;") {
TAG_ATTRS(TDiv, {{"class", "yaml-sticky-btn-wrap fold-yaml-config yaml-btn-3"}, {"title", "fold"}}) {
DIV_CLASS("yaml-sticky-btn") { }
}
TAG_ATTRS(TDiv, {{"class", "yaml-sticky-btn-wrap unfold-yaml-config yaml-btn-2"}, {"title", "unfold"}}) {
DIV_CLASS("yaml-sticky-btn") { }
}
TAG_ATTRS(TDiv, {{"class", "yaml-sticky-btn-wrap copy-yaml-config yaml-btn-1"}, {"title", "copy"}}) {
DIV_CLASS("yaml-sticky-btn") { }
}
DIV_CLASS("yaml-config-item") {
str << StartupStorageYaml;
}
}
}
} else {
str << "<div class=\"alert alert-info\" role=\"alert\">" << Endl;
str << "No storage config available. This is normal for non-seed-nodes initialization." << Endl;
str << "</div>" << Endl;
}
}
str << "<br />" << Endl;
COLLAPSED_REF_CONTENT("resolved-yaml-config", "Resolved YAML config") {
TAG_CLASS_STYLE(TDiv, "configs-dispatcher", "padding: 0 12px;") {
TAG_ATTRS(TDiv, {{"class", "yaml-sticky-btn-wrap fold-yaml-config yaml-btn-3"}, {"id", "fold-resolved-yaml-config"}, {"title", "fold"}}) {
Expand Down Expand Up @@ -818,9 +919,9 @@ void TConfigsDispatcher::Handle(TEvInterconnect::TEvNodesInfo::TPtr &ev)

class TConfigurationResult
: public IConfigurationResult
, public IStorageConfigResult
{
public:
// TODO make ref
const NKikimrConfig::TAppConfig& GetConfig() const override {
return Config;
}
Expand All @@ -829,10 +930,6 @@ class TConfigurationResult
return !MainYamlConfig.empty();
}

const TString& GetMainYamlConfig() const override {
return MainYamlConfig;
}

TMap<ui64, TString> GetVolatileYamlConfigs() const override {
return VolatileYamlConfigs;
}
Expand All @@ -845,10 +942,24 @@ class TConfigurationResult
return DatabaseYamlConfig;
}

const TString& GetStorageYamlConfig() const override {
return StorageYamlConfig;
}

const TString& GetSourceAddress() const override {
return SourceAddress;
}

const TString& GetMainYamlConfig() const override {
return MainYamlConfig;
}

NKikimrConfig::TAppConfig Config;
TString MainYamlConfig;
TMap<ui64, TString> VolatileYamlConfigs;
TString DatabaseYamlConfig;
TString StorageYamlConfig;
TString SourceAddress;
};

void TConfigsDispatcher::UpdateCandidateStartupConfig(TEvConsole::TEvConfigSubscriptionNotification::TPtr &ev)
Expand All @@ -864,16 +975,41 @@ try {

auto &rec = ev->Get()->Record;

auto dcClient = std::make_unique<TDynConfigClientMock>();
auto configs = std::make_shared<TConfigurationResult>();
dcClient->SavedResult = configs;
configs->Config = rec.GetRawConsoleConfig();
configs->MainYamlConfig = rec.GetMainYamlConfig();
if (rec.HasDatabaseYamlConfig()) {
configs->DatabaseYamlConfig = rec.GetDatabaseYamlConfig();
}
// TODO volatile
RecordedInitialConfiguratorDeps->DynConfigClient = std::move(dcClient);

auto configSource = DetermineConfigSource();

LastReplayUsedSeedNodesPath = false;
LastReplayUsedDynamicConfigPath = false;

switch (configSource) {
case EConfigSource::SeedNodes:
configs->StorageYamlConfig = StartupStorageYaml;
{
auto configClient = std::make_unique<TConfigClientMock>();
configClient->SavedResult = configs;
RecordedInitialConfiguratorDeps->ConfigClient = std::move(configClient);
}
LastReplayUsedSeedNodesPath = true;
break;

case EConfigSource::DynamicConfig:
case EConfigSource::Unknown:
{
auto dcClient = std::make_unique<TDynConfigClientMock>();
dcClient->SavedResult = configs;
RecordedInitialConfiguratorDeps->DynConfigClient = std::move(dcClient);
}
LastReplayUsedDynamicConfigPath = true;
break;
}

auto deps = RecordedInitialConfiguratorDeps->GetDeps();
NConfig::TInitialConfigurator initCfg(deps);

Expand Down Expand Up @@ -1051,9 +1187,11 @@ void TConfigsDispatcher::Handle(TEvConsole::TEvConfigSubscriptionNotification::T
}

if (CurrentStateFunc() == &TThis::StateInit) {
BLOG_D("Handle TEvConfigSubscriptionNotification: transitioning to StateWork");
Become(&TThis::StateWork);
ProcessEnqueuedEvents();
}
BLOG_D("Handle TEvConfigSubscriptionNotification: exit");
}

void TConfigsDispatcher::UpdateYamlVersion(const TSubscription::TPtr &subscription) const
Expand Down Expand Up @@ -1295,6 +1433,15 @@ void TConfigsDispatcher::Handle(TEvConsole::TEvGetNodeConfigurationVersionReques
Send(ev->Sender, response.release());
}

void TConfigsDispatcher::Handle(TEvConfigsDispatcher::TEvGetStateRequest::TPtr &ev) {
auto state = GetState();
Send(ev->Sender, new TEvConfigsDispatcher::TEvGetStateResponse(std::move(state)));
}

void TConfigsDispatcher::Handle(TEvConfigsDispatcher::TEvGetStorageYamlRequest::TPtr &ev) {
Send(ev->Sender, new TEvConfigsDispatcher::TEvGetStorageYamlResponse(StartupStorageYaml));
}

IActor *CreateConfigsDispatcher(const TConfigsDispatcherInitInfo& initInfo) {
return new TConfigsDispatcher(initInfo);
}
Expand Down
47 changes: 47 additions & 0 deletions ydb/core/cms/console/configs_dispatcher.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include "defs.h"
#include "configs_dispatcher_observer.h"

#include <ydb/core/protos/config.pb.h>
#include <ydb/core/config/init/init.h>
Expand Down Expand Up @@ -36,6 +37,12 @@ namespace TEvConfigsDispatcher {
EvGetConfigResponse,
EvRemoveConfigSubscriptionRequest,
EvRemoveConfigSubscriptionResponse,

// Observability events
EvGetStateRequest,
EvGetStateResponse,
EvGetStorageYamlRequest,
EvGetStorageYamlResponse,

EvEnd
};
Expand Down Expand Up @@ -106,6 +113,46 @@ namespace TEvConfigsDispatcher {
struct TEvGetConfigResponse : public TEventLocal<TEvGetConfigResponse, EvGetConfigResponse> {
std::shared_ptr<const NKikimrConfig::TAppConfig> Config;
};

/**
* Request current state of ConfigsDispatcher.
* Response: TEvGetStateResponse
*/
struct TEvGetStateRequest : public TEventLocal<TEvGetStateRequest, EvGetStateRequest> {
TEvGetStateRequest() = default;
};

/**
* Response containing current state snapshot.
*/
struct TEvGetStateResponse : public TEventLocal<TEvGetStateResponse, EvGetStateResponse> {
TConfigsDispatcherState State;

TEvGetStateResponse(TConfigsDispatcherState state)
: State(std::move(state))
{}
};

/**
* Request storage YAML config (if available).
* Only available if initialized from seed nodes.
* Response: TEvGetStorageYamlResponse
*/
struct TEvGetStorageYamlRequest : public TEventLocal<TEvGetStorageYamlRequest, EvGetStorageYamlRequest> {
TEvGetStorageYamlRequest() = default;
};

/**
* Response containing storage YAML config.
* StorageYaml will be empty if not initialized from seed nodes.
*/
struct TEvGetStorageYamlResponse : public TEventLocal<TEvGetStorageYamlResponse, EvGetStorageYamlResponse> {
TString StorageYaml;

TEvGetStorageYamlResponse(TString storageYaml)
: StorageYaml(std::move(storageYaml))
{}
};
};

/**
Expand Down
Loading
Loading