mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-18 13:42:02 +00:00
188 lines
5.4 KiB
C++
188 lines
5.4 KiB
C++
#include "ConfigReloader.h"
|
|
|
|
#include <Poco/Util/Application.h>
|
|
#include <Poco/File.h>
|
|
#include <common/logger_useful.h>
|
|
#include <Common/setThreadName.h>
|
|
#include "ConfigProcessor.h"
|
|
|
|
|
|
namespace DB
|
|
{
|
|
|
|
constexpr decltype(ConfigReloader::reload_interval) ConfigReloader::reload_interval;
|
|
|
|
ConfigReloader::ConfigReloader(
|
|
const std::string & path_,
|
|
const std::string & include_from_path_,
|
|
const std::string & preprocessed_dir_,
|
|
zkutil::ZooKeeperNodeCache && zk_node_cache_,
|
|
const zkutil::EventPtr & zk_changed_event_,
|
|
Updater && updater_,
|
|
bool already_loaded)
|
|
: path(path_), include_from_path(include_from_path_)
|
|
, preprocessed_dir(preprocessed_dir_)
|
|
, zk_node_cache(std::move(zk_node_cache_))
|
|
, zk_changed_event(zk_changed_event_)
|
|
, updater(std::move(updater_))
|
|
{
|
|
if (!already_loaded)
|
|
reloadIfNewer(/* force = */ true, /* throw_on_error = */ true, /* fallback_to_preprocessed = */ true);
|
|
}
|
|
|
|
|
|
void ConfigReloader::start()
|
|
{
|
|
thread = ThreadFromGlobalPool(&ConfigReloader::run, this);
|
|
}
|
|
|
|
|
|
ConfigReloader::~ConfigReloader()
|
|
{
|
|
try
|
|
{
|
|
quit = true;
|
|
zk_changed_event->set();
|
|
|
|
if (thread.joinable())
|
|
thread.join();
|
|
}
|
|
catch (...)
|
|
{
|
|
DB::tryLogCurrentException(__PRETTY_FUNCTION__);
|
|
}
|
|
}
|
|
|
|
|
|
void ConfigReloader::run()
|
|
{
|
|
setThreadName("ConfigReloader");
|
|
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
bool zk_changed = zk_changed_event->tryWait(std::chrono::milliseconds(reload_interval).count());
|
|
if (quit)
|
|
return;
|
|
|
|
reloadIfNewer(zk_changed, /* throw_on_error = */ false, /* fallback_to_preprocessed = */ false);
|
|
}
|
|
catch (...)
|
|
{
|
|
tryLogCurrentException(log, __PRETTY_FUNCTION__);
|
|
std::this_thread::sleep_for(reload_interval);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConfigReloader::reloadIfNewer(bool force, bool throw_on_error, bool fallback_to_preprocessed)
|
|
{
|
|
std::lock_guard lock(reload_mutex);
|
|
|
|
FilesChangesTracker new_files = getNewFileList();
|
|
if (force || need_reload_from_zk || new_files.isDifferOrNewerThan(files))
|
|
{
|
|
ConfigProcessor config_processor(path);
|
|
ConfigProcessor::LoadedConfig loaded_config;
|
|
try
|
|
{
|
|
LOG_DEBUG_FORMATTED(log, "Loading config '{}'", path);
|
|
|
|
loaded_config = config_processor.loadConfig(/* allow_zk_includes = */ true);
|
|
if (loaded_config.has_zk_includes)
|
|
loaded_config = config_processor.loadConfigWithZooKeeperIncludes(
|
|
zk_node_cache, zk_changed_event, fallback_to_preprocessed);
|
|
}
|
|
catch (const Coordination::Exception & e)
|
|
{
|
|
if (Coordination::isHardwareError(e.code))
|
|
need_reload_from_zk = true;
|
|
|
|
if (throw_on_error)
|
|
throw;
|
|
|
|
tryLogCurrentException(log, "ZooKeeper error when loading config from '" + path + "'");
|
|
return;
|
|
}
|
|
catch (...)
|
|
{
|
|
if (throw_on_error)
|
|
throw;
|
|
|
|
tryLogCurrentException(log, "Error loading config from '" + path + "'");
|
|
return;
|
|
}
|
|
config_processor.savePreprocessedConfig(loaded_config, preprocessed_dir);
|
|
|
|
/** We should remember last modification time if and only if config was sucessfully loaded
|
|
* Otherwise a race condition could occur during config files update:
|
|
* File is contain raw (and non-valid) data, therefore config is not applied.
|
|
* When file has been written (and contain valid data), we don't load new data since modification time remains the same.
|
|
*/
|
|
if (!loaded_config.loaded_from_preprocessed)
|
|
{
|
|
files = std::move(new_files);
|
|
need_reload_from_zk = false;
|
|
}
|
|
|
|
try
|
|
{
|
|
updater(loaded_config.configuration);
|
|
}
|
|
catch (...)
|
|
{
|
|
if (throw_on_error)
|
|
throw;
|
|
tryLogCurrentException(log, "Error updating configuration from '" + path + "' config.");
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ConfigReloader::FileWithTimestamp
|
|
{
|
|
std::string path;
|
|
time_t modification_time;
|
|
|
|
FileWithTimestamp(const std::string & path_, time_t modification_time_)
|
|
: path(path_), modification_time(modification_time_) {}
|
|
|
|
bool operator < (const FileWithTimestamp & rhs) const
|
|
{
|
|
return path < rhs.path;
|
|
}
|
|
|
|
static bool isTheSame(const FileWithTimestamp & lhs, const FileWithTimestamp & rhs)
|
|
{
|
|
return (lhs.modification_time == rhs.modification_time) && (lhs.path == rhs.path);
|
|
}
|
|
};
|
|
|
|
|
|
void ConfigReloader::FilesChangesTracker::addIfExists(const std::string & path_to_add)
|
|
{
|
|
if (!path_to_add.empty() && Poco::File(path_to_add).exists())
|
|
files.emplace(path_to_add, Poco::File(path_to_add).getLastModified().epochTime());
|
|
}
|
|
|
|
bool ConfigReloader::FilesChangesTracker::isDifferOrNewerThan(const FilesChangesTracker & rhs)
|
|
{
|
|
return (files.size() != rhs.files.size()) ||
|
|
!std::equal(files.begin(), files.end(), rhs.files.begin(), FileWithTimestamp::isTheSame);
|
|
}
|
|
|
|
ConfigReloader::FilesChangesTracker ConfigReloader::getNewFileList() const
|
|
{
|
|
FilesChangesTracker file_list;
|
|
|
|
file_list.addIfExists(path);
|
|
file_list.addIfExists(include_from_path);
|
|
|
|
for (const auto & merge_path : ConfigProcessor::getConfigMergeFiles(path))
|
|
file_list.addIfExists(merge_path);
|
|
|
|
return file_list;
|
|
}
|
|
|
|
}
|