From 1d279f8b2bed65b5a57f34d936fe5c43688a333e Mon Sep 17 00:00:00 2001 From: karteekmurthys Date: Fri, 22 Dec 2023 11:19:18 -0800 Subject: [PATCH 1/6] Expose Rest API to return metrics in prometheus format --- presto-native-execution/etc/config.properties | 6 +- .../presto_cpp/main/PrestoMain.cpp | 6 +- .../presto_cpp/main/PrestoServer.cpp | 14 ++ .../presto_cpp/main/PrestoServer.h | 2 + .../presto_cpp/main/common/CMakeLists.txt | 3 +- .../main/common/PrometheusStatsReporter.cpp | 111 +++++++++++++++ .../main/common/PrometheusStatsReporter.h | 129 ++++++++++++++++++ .../presto_cpp/main/tests/CMakeLists.txt | 5 + .../tests/PrometheusStatsReporterTest.cpp | 68 +++++++++ 9 files changed, 336 insertions(+), 8 deletions(-) create mode 100644 presto-native-execution/presto_cpp/main/common/PrometheusStatsReporter.cpp create mode 100644 presto-native-execution/presto_cpp/main/common/PrometheusStatsReporter.h create mode 100644 presto-native-execution/presto_cpp/main/tests/PrometheusStatsReporterTest.cpp diff --git a/presto-native-execution/etc/config.properties b/presto-native-execution/etc/config.properties index 73654217fa154..8f98a65ae9115 100644 --- a/presto-native-execution/etc/config.properties +++ b/presto-native-execution/etc/config.properties @@ -1,5 +1,7 @@ -discovery.uri=http://127.0.0.1:58215 +discovery.uri=http://127.0.0.1:8080 presto.version=testversion http-server.http.port=7777 shutdown-onset-sec=1 -register-test-functions=true \ No newline at end of file +register-test-functions=true +query.max-memory-per-node=30GB +task.max-drivers-per-task=1 \ No newline at end of file diff --git a/presto-native-execution/presto_cpp/main/PrestoMain.cpp b/presto-native-execution/presto_cpp/main/PrestoMain.cpp index 9e74b5b1dacfd..554677a83801d 100644 --- a/presto-native-execution/presto_cpp/main/PrestoMain.cpp +++ b/presto-native-execution/presto_cpp/main/PrestoMain.cpp @@ -27,11 +27,7 @@ int main(int argc, char* argv[]) { google::InstallFailureSignalHandler(); PRESTO_STARTUP_LOG(INFO) << "Entering main()"; facebook::presto::PrestoServer presto(FLAGS_etc_dir); + facebook::velox::BaseStatsReporter::registered = true; presto.run(); PRESTO_SHUTDOWN_LOG(INFO) << "Exiting main()"; } - -// Initialize singleton for the reporter. -folly::Singleton reporter([]() { - return new facebook::velox::DummyStatsReporter(); -}); diff --git a/presto-native-execution/presto_cpp/main/PrestoServer.cpp b/presto-native-execution/presto_cpp/main/PrestoServer.cpp index 16eb451b24673..6b9982e5de292 100644 --- a/presto-native-execution/presto_cpp/main/PrestoServer.cpp +++ b/presto-native-execution/presto_cpp/main/PrestoServer.cpp @@ -24,6 +24,7 @@ #include "presto_cpp/main/TaskResource.h" #include "presto_cpp/main/common/ConfigReader.h" #include "presto_cpp/main/common/Counters.h" +#include "presto_cpp/main/common/PrometheusStatsReporter.h" #include "presto_cpp/main/common/Utils.h" #include "presto_cpp/main/http/filters/AccessLogFilter.h" #include "presto_cpp/main/http/filters/HttpEndpointLatencyFilter.h" @@ -319,6 +320,14 @@ void PrestoServer::run() { proxygen::ResponseHandler* downstream) { server->reportServerInfo(downstream); }); + httpServer_->registerGet( + "/v1/info/health/metrics", + [server = this]( + proxygen::HTTPMessage* /*message*/, + const std::vector>& /*body*/, + proxygen::ResponseHandler* downstream) { + server->reportHealthMetrics(downstream); + }); httpServer_->registerGet( "/v1/info/state", [server = this]( @@ -1093,6 +1102,11 @@ void PrestoServer::reportServerInfo(proxygen::ResponseHandler* downstream) { http::sendOkResponse(downstream, json(serverInfo)); } +void PrestoServer::reportHealthMetrics(proxygen::ResponseHandler* downstream) { + auto reporter = std::dynamic_pointer_cast( + folly::Singleton::try_get()); + http::sendOkResponse(downstream, reporter->getMetricsForPrometheus()); +} void PrestoServer::reportNodeStatus(proxygen::ResponseHandler* downstream) { http::sendOkResponse(downstream, json(fetchNodeStatus())); } diff --git a/presto-native-execution/presto_cpp/main/PrestoServer.h b/presto-native-execution/presto_cpp/main/PrestoServer.h index 1fe61f52a9847..e70ca07040e4d 100644 --- a/presto-native-execution/presto_cpp/main/PrestoServer.h +++ b/presto-native-execution/presto_cpp/main/PrestoServer.h @@ -169,6 +169,8 @@ class PrestoServer { void reportNodeStatus(proxygen::ResponseHandler* downstream); + void reportHealthMetrics(proxygen::ResponseHandler* downstream); + protocol::NodeStatus fetchNodeStatus(); void populateMemAndCPUInfo(); diff --git a/presto-native-execution/presto_cpp/main/common/CMakeLists.txt b/presto-native-execution/presto_cpp/main/common/CMakeLists.txt index 153883e23d531..1c511121f3adb 100644 --- a/presto-native-execution/presto_cpp/main/common/CMakeLists.txt +++ b/presto-native-execution/presto_cpp/main/common/CMakeLists.txt @@ -11,7 +11,8 @@ # limitations under the License. add_library(presto_exception Exception.cpp) -add_library(presto_common Counters.cpp Utils.cpp ConfigReader.cpp Configs.cpp) +add_library(presto_common Counters.cpp Utils.cpp ConfigReader.cpp Configs.cpp + PrometheusStatsReporter.cpp) target_link_libraries(presto_exception velox_exception) set_property(TARGET presto_exception PROPERTY JOB_POOL_LINK diff --git a/presto-native-execution/presto_cpp/main/common/PrometheusStatsReporter.cpp b/presto-native-execution/presto_cpp/main/common/PrometheusStatsReporter.cpp new file mode 100644 index 0000000000000..c88bf65d166e6 --- /dev/null +++ b/presto-native-execution/presto_cpp/main/common/PrometheusStatsReporter.cpp @@ -0,0 +1,111 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PrometheusStatsReporter.h" + +namespace facebook::presto { + +void PrometheusStatsReporter::registerMetricExportType( + folly::StringPiece key, + facebook::velox::StatType statType) const { + registerMetricExportType(key.start(), statType); +} + +void PrometheusStatsReporter::registerMetricExportType( + const char* key, + facebook::velox::StatType statType) const { + std::lock_guard lock(mutex_); + registeredStats_.emplace(key, statType); + if (statType == velox::StatType::COUNT) { + counterMetrics.emplace(key, 0); + } + if (statType == velox::StatType::SUM) { + gaugeMetrics.emplace(key, std::vector>()); + } +} + +const uint64_t getCurrentEpochTimestamp() { + auto p1 = std::chrono::system_clock::now(); + return std::chrono::duration_cast(p1.time_since_epoch()) + .count(); +} + +void PrometheusStatsReporter::addMetricValue(const char* key, size_t value) + const { + auto it = registeredStats_.find(key); + if (it == registeredStats_.end()) { + VLOG(2) << "addMetricValue() for unregistered stat"; + return; + } + if (it->second == facebook::velox::StatType::COUNT) { + std::lock_guard lock(mutex_); + auto countItr = counterMetrics.find(key); + if (countItr == counterMetrics.end()) { + counterMetrics.emplace(key, 1); + return; + } + countItr->second++; + return; + } + std::lock_guard lock(mutex_); + std::shared_ptr metric = std::make_shared(); + metric->timestamp = getCurrentEpochTimestamp(); + metric->value = value; + gaugeMetrics[key].push_back(std::move(metric)); +} + +void PrometheusStatsReporter::addMetricValue( + const std::string& key, + size_t value) const { + addMetricValue(key.c_str(), value); +} + +void PrometheusStatsReporter::addMetricValue( + folly::StringPiece key, + size_t value) const { + addMetricValue(key.start(), value); +} + +const std::string PrometheusStatsReporter::getMetricsForPrometheus() { + std::lock_guard lock(mutex_); + std::stringstream ss; + for (const auto gauge : gaugeMetrics) { + auto metricName = gauge.first.substr(gauge.first.find(".") + 1); + ss << "# HELP " << metricName << std::endl; + ss << "# TYPE " << metricName << " gauge" << std::endl; + for (auto metric : gauge.second) { + ss << metricName << "{cluster=\"" << cluster_ << "\"" + << ",worker=\"" << workerPod_ << "\"} " << metric->value << " " + << metric->timestamp * 1000 << std::endl; + } + std::vector> metricsVector = gauge.second; + metricsVector.clear(); + } + + for (const auto counter : counterMetrics) { + auto metricName = counter.first.substr(counter.first.find(".") + 1); + ss << "# HELP " << metricName << std::endl; + ss << "# TYPE " << metricName << " " + << "counter" << std::endl; + ss << metricName << "{cluster=\"" << cluster_ << "\",worker=\"" + << workerPod_ << "\"} " << counter.second << std::endl; + } + return ss.str(); +} + +// Initialize singleton for the reporter +folly::Singleton reporter([]() { + return new PrometheusStatsReporter(); +}); +} // namespace facebook::presto diff --git a/presto-native-execution/presto_cpp/main/common/PrometheusStatsReporter.h b/presto-native-execution/presto_cpp/main/common/PrometheusStatsReporter.h new file mode 100644 index 0000000000000..be6e44b2de478 --- /dev/null +++ b/presto-native-execution/presto_cpp/main/common/PrometheusStatsReporter.h @@ -0,0 +1,129 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "presto_cpp/main/common/Configs.h" +#include "presto_cpp/main/common/Counters.h" +#include "velox/common/base/StatsReporter.h" +#include "velox/velox/common/base/Exceptions.h" + +namespace facebook::presto { + +class PrometheusStatsReporter : public facebook::velox::BaseStatsReporter { + public: + PrometheusStatsReporter( + const std::string cluster = "", + const std::string worker = "") { + if (cluster.empty()) { + auto nodeConfig = facebook::presto::NodeConfig::instance(); + cluster_ = nodeConfig->nodeEnvironment(); + } else { + cluster_ = cluster; + } + char* hostName = std::getenv("HOSTNAME"); + workerPod_ = !hostName ? worker : hostName; + } + + struct Gauge { + uint64_t timestamp; // Epoch timestamp. + size_t value; // Metric value. + }; + + /// Register a stat of the given stat type. + /// @param key The key to identify the stat. + /// @param statType How the stat is aggregated. + void registerMetricExportType( + const char* key, + facebook::velox::StatType statType) const override; + + void registerMetricExportType( + folly::StringPiece key, + facebook::velox::StatType statType) const override; + + void registerHistogramMetricExportType( + const char* /*key*/, + int64_t /* bucketWidth */, + int64_t /* min */, + int64_t /* max */, + const std::vector& /* pcts */) const override {} + + void registerHistogramMetricExportType( + folly::StringPiece /* key */, + int64_t /* bucketWidth */, + int64_t /* min */, + int64_t /* max */, + const std::vector& /* pcts */) const override {} + + void addMetricValue(const std::string& key, size_t value = 1) const override; + + void addMetricValue(const char* key, size_t value = 1) const override; + + void addMetricValue(folly::StringPiece key, size_t value = 1) const override; + + virtual void addHistogramMetricValue(const std::string& key, size_t value) + const override {} + + virtual void addHistogramMetricValue(const char* key, size_t value) + const override {} + + virtual void addHistogramMetricValue(folly::StringPiece key, size_t value) + const override {} + + const facebook::velox::StatType getRegisteredStatType( + const std::string& metricName) { + std::lock_guard lock(mutex_); + return registeredStats_[metricName]; + } + + /* + * Serializes the metrics collected so far in the format suitable for + * back filling Prometheus server. + * + * Given a metric name and a set of labels, time series are frequently + * identified using this notation: + * + * {