diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index c99543ff631..a0dfb80b037 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -861,7 +861,8 @@ int Server::main(const std::vector & /*args*/) }; /// This object will periodically calculate some metrics. - AsynchronousMetrics async_metrics(*global_context); + AsynchronousMetrics async_metrics(*global_context, + config().getUInt("asynchronous_metrics_update_period_s", 60)); attachSystemTablesAsync(*DatabaseCatalog::instance().getSystemDatabase(), async_metrics); for (const auto & listen_host : listen_hosts) diff --git a/src/IO/ReadHelpers.h b/src/IO/ReadHelpers.h index 7254ae43c17..f299ab2286f 100644 --- a/src/IO/ReadHelpers.h +++ b/src/IO/ReadHelpers.h @@ -454,6 +454,9 @@ void readBackQuotedString(String & s, ReadBuffer & buf); void readBackQuotedStringWithSQLStyle(String & s, ReadBuffer & buf); void readStringUntilEOF(String & s, ReadBuffer & buf); + +// Reads the line until EOL, unescaping backslash escape sequences. +// Buffer pointer is left at EOL, don't forget to advance it. void readEscapedStringUntilEOL(String & s, ReadBuffer & buf); diff --git a/src/Interpreters/AsynchronousMetrics.cpp b/src/Interpreters/AsynchronousMetrics.cpp index babe63f8522..eb93ac4412b 100644 --- a/src/Interpreters/AsynchronousMetrics.cpp +++ b/src/Interpreters/AsynchronousMetrics.cpp @@ -58,48 +58,44 @@ AsynchronousMetricValues AsynchronousMetrics::getValues() const return values; } +static auto get_next_update_time(std::chrono::seconds update_period) +{ + using namespace std::chrono; + + const auto now = time_point_cast(system_clock::now()); + + // Use seconds since the start of the hour, because we don't know when + // the epoch started, maybe on some weird fractional time. + const auto start_of_hour = time_point_cast(time_point_cast(now)); + const auto seconds_passed = now - start_of_hour; + + // Rotate time forward by half a period -- e.g. if a period is a minute, + // we'll collect metrics on start of minute + 30 seconds. This is to + // achieve temporal separation with MetricTransmitter. Don't forget to + // rotate it back. + const auto rotation = update_period / 2; + + const auto periods_passed = (seconds_passed + rotation) / update_period; + const auto seconds_next = (periods_passed + 1) * update_period - rotation; + const auto time_next = start_of_hour + seconds_next; + + return time_next; +} void AsynchronousMetrics::run() { setThreadName("AsyncMetrics"); - const auto get_next_update_time = [] - { - using namespace std::chrono; - - // The period doesn't really have to be configurable, but sometimes you - // need to change it by recompilation to debug something. The generic - // code is left here so that you don't have to ruin your mood by touching - // std::chrono. - const seconds period(60); - - const auto now = time_point_cast(system_clock::now()); - - // Use seconds since the start of the hour, because we don't know when - // the epoch started, maybe on some weird fractional time. - const auto start_of_hour = time_point_cast(time_point_cast(now)); - const auto seconds_passed = now - start_of_hour; - - // Rotate time forward by half a period -- e.g. if a period is a minute, - // we'll collect metrics on start of minute + 30 seconds. This is to - // achieve temporal separation with MetricTransmitter. Don't forget to - // rotate it back. - const auto rotation = period / 2; - - const auto periods_passed = (seconds_passed + rotation) / period; - const auto seconds_next = (periods_passed + 1) * period - rotation; - const auto time_next = start_of_hour + seconds_next; - - return time_next; - }; - while (true) { { // Wait first, so that the first metric collection is also on even time. std::unique_lock lock{mutex}; - if (wait_cond.wait_until(lock, get_next_update_time(), [this] { return quit; })) + if (wait_cond.wait_until(lock, get_next_update_time(update_period), + [this] { return quit; })) + { break; + } } try @@ -329,6 +325,48 @@ void AsynchronousMetrics::update() saveAllArenasMetric(new_values, "muzzy_purged"); #endif + // Try to add processor frequencies, ignoring errors. + try + { + ReadBufferFromFile buf("/proc/cpuinfo", 32768 /* buf_size */); + + // We need the following lines: + // core id : 4 + // cpu MHz : 4052.941 + // They contain tabs and are interspersed with other info. + int core_id = 0; + while (!buf.eof()) + { + std::string s; + // We don't have any backslash escape sequences in /proc/cpuinfo, so + // this function will read the line until EOL, which is exactly what + // we need. + readEscapedStringUntilEOL(s, buf); + // It doesn't read the EOL itself. + ++buf.position(); + + if (s.rfind("core id", 0) == 0) + { + if (auto colon = s.find_first_of(':')) + { + core_id = std::stoi(s.substr(colon + 2)); + } + } + else if (s.rfind("cpu MHz", 0) == 0) + { + if (auto colon = s.find_first_of(':')) + { + auto mhz = std::stod(s.substr(colon + 2)); + new_values[fmt::format("CPUFrequencyMHz_{}", core_id)] = mhz; + } + } + } + } + catch (...) + { + tryLogCurrentException(__PRETTY_FUNCTION__); + } + /// Add more metrics as you wish. // Log the new metrics. diff --git a/src/Interpreters/AsynchronousMetrics.h b/src/Interpreters/AsynchronousMetrics.h index 6817f545c8f..6ab32ff9ab6 100644 --- a/src/Interpreters/AsynchronousMetrics.h +++ b/src/Interpreters/AsynchronousMetrics.h @@ -18,15 +18,20 @@ typedef double AsynchronousMetricValue; typedef std::unordered_map AsynchronousMetricValues; -/** Periodically (each minute, starting at 30 seconds offset) +/** Periodically (by default, each minute, starting at 30 seconds offset) * calculates and updates some metrics, * that are not updated automatically (so, need to be asynchronously calculated). */ class AsynchronousMetrics { public: - AsynchronousMetrics(Context & context_) - : context(context_), thread([this] { run(); }) + // The default value of update_period_seconds is for ClickHouse-over-YT + // in Arcadia -- it uses its own server implementation that also uses these + // metrics. + AsynchronousMetrics(Context & context_, int update_period_seconds = 60) + : context(context_), + update_period(update_period_seconds), + thread([this] { run(); }) { } @@ -38,6 +43,7 @@ public: private: Context & context; + const std::chrono::seconds update_period; mutable std::mutex mutex; std::condition_variable wait_cond;