From 10f99c3a99d4137ebcc7e603c908215c2f226bcb Mon Sep 17 00:00:00 2001 From: t-horikawa Date: Wed, 15 May 2024 09:12:38 +0900 Subject: [PATCH] implement a metrics subsystem --- src/CMakeLists.txt | 4 +- src/tateyama/metrics/metrics.cpp | 216 ++++++++++++ .../statistics.h => metrics/metrics.h} | 4 +- src/tateyama/proto/metrics/request.proto | 39 +++ src/tateyama/proto/metrics/response.proto | 56 ++++ src/tateyama/statistics/statistics_mock.cpp | 145 --------- src/tateyama/tgctl/main.cpp | 6 +- src/tateyama/transport/transport.h | 52 ++- test/CMakeLists.txt | 1 + test/tateyama/metrics/metrics_test.cpp | 308 ++++++++++++++++++ .../tateyama/monitor/statistics_mock_test.cpp | 69 ---- test/test_root.h | 15 + 12 files changed, 694 insertions(+), 221 deletions(-) create mode 100644 src/tateyama/metrics/metrics.cpp rename src/tateyama/{statistics/statistics.h => metrics/metrics.h} (92%) create mode 100644 src/tateyama/proto/metrics/request.proto create mode 100644 src/tateyama/proto/metrics/response.proto delete mode 100644 src/tateyama/statistics/statistics_mock.cpp create mode 100644 test/tateyama/metrics/metrics_test.cpp delete mode 100644 test/tateyama/monitor/statistics_mock_test.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 656cfea..3652937 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -81,6 +81,8 @@ set(ProtoFiles ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/datastore/common.proto ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/endpoint/request.proto ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/endpoint/response.proto + ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/metrics/request.proto + ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/metrics/response.proto ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/framework/request.proto ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/framework/response.proto ${CMAKE_CURRENT_SOURCE_DIR}/tateyama/proto/kvs/response.proto @@ -107,7 +109,7 @@ file(GLOB TGCTL_SOURCES "tateyama/configuration/*.cpp" "tateyama/utils/*.cpp" "tateyama/session/*.cpp" - "tateyama/statistics/*.cpp" + "tateyama/metrics/*.cpp" ) set_source_files_properties( diff --git a/src/tateyama/metrics/metrics.cpp b/src/tateyama/metrics/metrics.cpp new file mode 100644 index 0000000..de8600d --- /dev/null +++ b/src/tateyama/metrics/metrics.cpp @@ -0,0 +1,216 @@ +/* + * Copyright 2022-2024 Project Tsurugi. + * + * 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 +#include + +#define BOOST_BIND_GLOBAL_PLACEHOLDERS // to retain the current behavior +#include +#include + +#include "tateyama/authentication/authentication.h" +#include +#include "tateyama/monitor/monitor.h" + +#include +#include + +#include "metrics.h" + +DECLARE_string(monitor); // NOLINT +DECLARE_string(format); // NOLINT + +namespace tateyama::metrics { +using namespace std::string_view_literals; + +tgctl::return_code list() { + std::unique_ptr monitor_output{}; + if (!FLAGS_monitor.empty()) { + monitor_output = std::make_unique(FLAGS_monitor); + monitor_output->start(); + } + + authentication::auth_options(); + try { + auto transport = std::make_unique(tateyama::framework::service_id_metrics); + ::tateyama::proto::metrics::request::Request request{}; + request.mutable_list(); + auto response_opt = transport->send<::tateyama::proto::metrics::response::MetricsInformation>(request); + request.clear_list(); + + if (response_opt) { + auto&& info_list = response_opt.value(); + + if (FLAGS_format == "json") { + std::cout << "{" << std::endl; + int len = info_list.items_size(); + for (int i = 0; i < len; i++) { + auto&& item = info_list.items(i); + std::cout << " \"" << item.key() << "\": \"" << item.description() << "\""; + if (i == (len - 1)) { + std::cout << std::endl; + } else { + std::cout << "," << std::endl; + } + } + std::cout << "}" << std::endl; + + if (monitor_output) { + monitor_output->finish(true); + } + return tateyama::tgctl::return_code::ok; + } + if (FLAGS_format == "text") { + std::size_t key_max{0}; + std::size_t description_max{0}; + int len = info_list.items_size(); + for (int i = 0; i < len; i++) { + auto&& item = info_list.items(i); + + if (key_max < item.key().length()) { + key_max = item.key().length(); + } + if (description_max < item.description().length()) { + description_max = item.description().length(); + } + } + + std::string prev_key{}; + for (int i = 0; i < len; i++) { + auto&& item = info_list.items(i); + if (prev_key != item.key()) { + std::cout << std::right; + std::cout << std::setw(static_cast(key_max)) << item.key(); + std::cout << " : "; + std::cout << std::left; + std::cout << std::setw(static_cast(description_max)) << item.description(); + std::cout << std::endl; + prev_key = item.key(); + } + } + + if (monitor_output) { + monitor_output->finish(true); + } + return tateyama::tgctl::return_code::ok; + } + std::cerr << "format " << FLAGS_format << " is not supported" << std::endl; + } else { + std::cerr << "could not receive a valid response" << std::endl; + } + } catch (std::runtime_error &ex) { + std::cerr << "could not connect to database with name " << tateyama::bootstrap::wire::transport::database_name() << std::endl; + } + + if (monitor_output) { + monitor_output->finish(false); + } + return tgctl::return_code::err; +} + +tgctl::return_code show() { // NOLINT(readability-function-cognitive-complexity) + std::unique_ptr monitor_output{}; + if (!FLAGS_monitor.empty()) { + monitor_output = std::make_unique(FLAGS_monitor); + monitor_output->start(); + } + + authentication::auth_options(); + try { + auto transport = std::make_unique(tateyama::framework::service_id_metrics); + ::tateyama::proto::metrics::request::Request request{}; + request.mutable_show(); + auto response_opt = transport->send<::tateyama::proto::metrics::response::MetricsInformation>(request); + request.clear_show(); + + if (response_opt) { + auto&& info_show = response_opt.value(); + std::string separator{}; + + if (FLAGS_format == "json") { + std::cout << "{" << std::endl; + int len = info_show.items_size(); + for (int i = 0; i < len; i++) { + auto&& item = info_show.items(i); + auto&& key = item.key(); + auto&& value = item.value(); + if (0 < i && i < len) { + std::cout << "," << std::endl; + } + if (value.value_or_array_case() == ::tateyama::proto::metrics::response::MetricsValue::ValueOrArrayCase::kArray) { + auto&& array = value.array(); + int alen = array.elements_size(); + std::cout << " \"" << item.key() << "\": [" << std::endl; + separator = " ]"; + for (int j = 0; j < alen; j++) { + auto&& element = array.elements(j); + std::cout << " {" << std::endl; + auto&& attributes = element.attributes(); + for (auto&& itr = attributes.cbegin(); itr != attributes.cend(); itr++) { + std::cout << " \"" << itr->first << "\": \"" << itr->second << "\"," << std::endl; + } + if (element.value() > 1.0) { + std::cout.precision(0); + } else { + std::cout.precision(6); + } + std::cout << " \"value\": " << std::fixed << element.value() << std::endl; + std::cout << ((j < (alen - 1)) ? " }," : " }" ) << std::endl; + } + } else { + auto v = value.value(); + if (v - static_cast(static_cast(v)) > 0.0) { + std::cout.precision(6); + } else { + std::cout.precision(0); + } + std::cout << " \"" << key << "\": " << std::fixed << value.value(); + } + if (!separator.empty()) { + std::cout << separator; + separator = ""; + } + } + std::cout << std::endl << "}" << std::endl; + + if (monitor_output) { + monitor_output->finish(true); + } + + return tateyama::tgctl::return_code::ok; + } + if (FLAGS_format == "text") { + std::cerr << "human readable format has not been supported" << std::endl; + } else { + std::cerr << "format " << FLAGS_format << " is not supported" << std::endl; + } + } else { + std::cerr << "could not receive a valid response" << std::endl; + } + } catch (std::runtime_error &ex) { + std::cerr << "could not connect to database with name " << tateyama::bootstrap::wire::transport::database_name() << std::endl; + } + + if (monitor_output) { + monitor_output->finish(false); + } + return tgctl::return_code::err; +} + +} // tateyama::metrics diff --git a/src/tateyama/statistics/statistics.h b/src/tateyama/metrics/metrics.h similarity index 92% rename from src/tateyama/statistics/statistics.h rename to src/tateyama/metrics/metrics.h index 1a7e545..5b5c18c 100644 --- a/src/tateyama/statistics/statistics.h +++ b/src/tateyama/metrics/metrics.h @@ -17,9 +17,9 @@ #include "tateyama/tgctl/tgctl.h" -namespace tateyama::statistics { +namespace tateyama::metrics { tgctl::return_code list(); tgctl::return_code show(); -} // tateyama::statistics +} // tateyama::metrics diff --git a/src/tateyama/proto/metrics/request.proto b/src/tateyama/proto/metrics/request.proto new file mode 100644 index 0000000..2052441 --- /dev/null +++ b/src/tateyama/proto/metrics/request.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; + +package tateyama.proto.metrics.request; + +option java_multiple_files = false; +option java_package = "com.tsurugidb.metrics.proto"; +option java_outer_classname = "MetricsRequest"; + +// the request message to metrics pseudo service. +message Request { + // service message version (major) + uint64 service_message_version_major = 1; + + // service message version (minor) + uint64 service_message_version_minor = 2; + + // reserved for system use + reserved 3 to 10; + + // the request command. + oneof command { + // show operation. + Show show = 11; + + // list operation. + List list = 12; + } + reserved 13 to 99; +} + +// show operation. +message Show { + // no special properties. +} + +// list operation. +message List { + // no special properties. +} diff --git a/src/tateyama/proto/metrics/response.proto b/src/tateyama/proto/metrics/response.proto new file mode 100644 index 0000000..c023144 --- /dev/null +++ b/src/tateyama/proto/metrics/response.proto @@ -0,0 +1,56 @@ +syntax = "proto3"; + +package tateyama.proto.metrics.response; + +option java_multiple_files = false; +option java_package = "com.tsurugidb.metrics.proto"; +option java_outer_classname = "MetricsResponse"; + +// the metrics value. +message MetricsValue { + // variant of metrics value, or empty if it is not set. + oneof value_or_array { + // just a value. + double value = 1; + + // sub-elements. + MetricsElementArray array = 2; + } +} + +// a variation of metrics value that holds multiple elements. +message MetricsElementArray { + + // the metrics elements. + repeated MetricsElement elements = 1; +} + +// an element of MetricsElementArray. +message MetricsElement { + + // the metrics element value. + double value = 1; + + // attributes of this metrics element. + map attributes = 2; +} + +// represents an item of metrics. +message MetricsItem { + + // the metrics item key. + string key = 1; + + // human-readable description of the item. + string description = 2; + + // the value of this item. + MetricsValue value = 3; +} + +// represents metrics information. +message MetricsInformation { + + // the individual metrics items. + repeated MetricsItem items = 1; +} diff --git a/src/tateyama/statistics/statistics_mock.cpp b/src/tateyama/statistics/statistics_mock.cpp deleted file mode 100644 index f0eb60c..0000000 --- a/src/tateyama/statistics/statistics_mock.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2022-2024 Project Tsurugi. - * - * 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 - -#define BOOST_BIND_GLOBAL_PLACEHOLDERS // to retain the current behavior -#include -#include - -#include "tateyama/monitor/monitor.h" -#include "statistics.h" - -DECLARE_string(monitor); // NOLINT -DECLARE_string(format); // NOLINT - -namespace tateyama::statistics { -using namespace std::string_view_literals; - -static constexpr std::string_view list_result = - "{\n" - " \"session_count\": \"number of active sessions\",\n" - " \"table_count\": \"number of user tables\",\n" - " \"index_size\": \"estimated each index size\",\n" - " \"storage_log_size\": \"transaction log disk usage\",\n" - " \"ipc_buffer_size\": \"allocated buffer size for all IPC sessions\",\n" - " \"sql_buffer_size\": \"allocated buffer size for SQL execution engine\"\n" - "}\n"sv; - -static constexpr std::string_view show_result = - "{\n" - " \"session_count\": 100,\n" - " \"table_count\": 3,\n" - " \"index_size\": [\n" - " {\n" - " \"table_name\": \"A\",\n" - " \"index_name\": \"IA1\",\n" - " \"value\": 65536\n" - " },\n" - " {\n" - " \"table_name\": \"A\",\n" - " \"index_name\": \"IA2\",\n" - " \"value\": 16777216\n" - " },\n" - " {\n" - " \"table_name\": \"B\",\n" - " \"index_name\": \"IB1\",\n" - " \"value\": 256\n" - " }\n" - " ],\n" - " \"storage_log_size\": 65535,\n" - " \"ipc_buffer_size\": 1024,\n" - " \"sql_buffer_size\": 268435456\n" - "}\n"sv; - -static std::string replaceString(const std::string& target, - const std::string& from, - const std::string& to) { - std::string result = target; - std::string::size_type pos = 0; - while(pos = result.find(from, pos), pos != std::string::npos) { - result.replace(pos, from.length(), to); - pos += to.length(); - } - return result; -} - -static std::string escape(std::string s) { - s.erase(std::remove(s.begin(), s.end(), '\n'), s.end()); - return replaceString(s, "\"", "\\\""); -} - -tgctl::return_code list() { - std::unique_ptr monitor_output{}; - if (!FLAGS_monitor.empty()) { - monitor_output = std::make_unique(FLAGS_monitor); - monitor_output->start(); - } - - if (FLAGS_format == "json") { - std::cout << list_result; - std::cout << std::flush; - if (monitor_output) { - std::stringstream si{std::string(list_result)}; - boost::property_tree::ptree pt{}; - boost::property_tree::read_json(si, pt); - std::stringstream so{}; - boost::property_tree::write_json(so, pt, false); - monitor_output->dbstats_description(escape(so.str())); - monitor_output->finish(true); - } - return tateyama::tgctl::return_code::ok; - } - std::cerr << "format " << FLAGS_format << " is not supported" << std::endl; - if (monitor_output) { - monitor_output->finish(false); - } - return tateyama::tgctl::return_code::err; -} - -tgctl::return_code show() { - std::unique_ptr monitor_output{}; - if (!FLAGS_monitor.empty()) { - monitor_output = std::make_unique(FLAGS_monitor); - monitor_output->start(); - } - - if (FLAGS_format == "json") { - std::cout << show_result; - std::cout << std::flush; - if (monitor_output) { - std::stringstream si{std::string(show_result)}; - boost::property_tree::ptree pt{}; - boost::property_tree::read_json(si, pt); - std::stringstream so{}; - boost::property_tree::write_json(so, pt, false); - std::string s = so.str(); - monitor_output->dbstats(escape(so.str())); - monitor_output->finish(true); - } - return tateyama::tgctl::return_code::ok; - } - std::cerr << "format " << FLAGS_format << " is not supported" << std::endl; - if (monitor_output) { - monitor_output->finish(false); - } - return tateyama::tgctl::return_code::err; -} - -} // tateyama::statistics diff --git a/src/tateyama/tgctl/main.cpp b/src/tateyama/tgctl/main.cpp index a86f9ce..422aae9 100644 --- a/src/tateyama/tgctl/main.cpp +++ b/src/tateyama/tgctl/main.cpp @@ -24,7 +24,7 @@ #include "tateyama/configuration/bootstrap_configuration.h" #include "tateyama/version/version.h" #include "tateyama/session/session.h" -#include "tateyama/statistics/statistics.h" +#include "tateyama/metrics/metrics.h" // common DEFINE_string(conf, "", "the file name of the configuration"); // NOLINT @@ -192,10 +192,10 @@ int tgctl_main(const std::vector& args) { //NOLINT(readability-func } if (args.at(1) == "dbstats") { if (args.at(2) == "list") { - return tateyama::statistics::list(); + return tateyama::metrics::list(); } if (args.at(2) == "show") { - return tateyama::statistics::show(); + return tateyama::metrics::show(); } std::cerr << "unknown dbstats-sub command '" << args.at(2) << "'" << std::endl; return tateyama::tgctl::return_code::err; diff --git a/src/tateyama/transport/transport.h b/src/tateyama/transport/transport.h index 83ef1f9..ba84d5d 100644 --- a/src/tateyama/transport/transport.h +++ b/src/tateyama/transport/transport.h @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include "tateyama/server/status_info.h" #include "client_wire.h" @@ -50,6 +52,8 @@ constexpr static std::size_t ENDPOINT_MESSAGE_VERSION_MAJOR = 0; constexpr static std::size_t ENDPOINT_MESSAGE_VERSION_MINOR = 0; constexpr static std::size_t SESSION_MESSAGE_VERSION_MAJOR = 0; constexpr static std::size_t SESSION_MESSAGE_VERSION_MINOR = 0; +constexpr static std::size_t METRICS_MESSAGE_VERSION_MAJOR = 0; +constexpr static std::size_t METRICS_MESSAGE_VERSION_MINOR = 0; class transport { public: @@ -87,7 +91,6 @@ class transport { transport(transport&& other) noexcept = delete; transport& operator=(transport&& other) noexcept = delete; - // for datastore template std::optional send(::tateyama::proto::datastore::request::Request& request) { auto& response_wire = wire_.get_response_wire(); @@ -233,6 +236,53 @@ class transport { return response; } + // metrics + template + std::optional send(::tateyama::proto::metrics::request::Request& request) { + auto& response_wire = wire_.get_response_wire(); + + std::stringstream sst{}; + if(auto res = tateyama::utils::SerializeDelimitedToOstream(header_, std::addressof(sst)); ! res) { + return std::nullopt; + } + request.set_service_message_version_major(METRICS_MESSAGE_VERSION_MAJOR); + request.set_service_message_version_minor(METRICS_MESSAGE_VERSION_MINOR); + if(auto res = tateyama::utils::SerializeDelimitedToOstream(request, std::addressof(sst)); ! res) { + return std::nullopt; + } + wire_.write(sst.str()); + + while (true) { + try { + response_wire.await(); + break; + } catch (std::runtime_error &e) { + if (status_info_->alive()) { + continue; + } + std::cerr << e.what() << std::endl; + return std::nullopt; + } + } + std::string res_message{}; + res_message.resize(response_wire.get_length()); + response_wire.read(res_message.data()); + ::tateyama::proto::framework::response::Header header{}; + google::protobuf::io::ArrayInputStream ins{res_message.data(), static_cast(res_message.length())}; + if(auto res = tateyama::utils::ParseDelimitedFromZeroCopyStream(std::addressof(header), std::addressof(ins), nullptr); ! res) { + return std::nullopt; + } + std::string_view payload{}; + if (auto res = tateyama::utils::GetDelimitedBodyFromZeroCopyStream(std::addressof(ins), nullptr, payload); ! res) { + return std::nullopt; + } + T response{}; + if(auto res = response.ParseFromArray(payload.data(), payload.length()); ! res) { + return std::nullopt; + } + return response; + } + void close() { wire_.close(); closed_ = true; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e9941d4..9e4a9b3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -42,6 +42,7 @@ file(GLOB SRCS "tateyama/configuration/*_test.cpp" "tateyama/version/*_test.cpp" "tateyama/session/*_test.cpp" + "tateyama/metrics/*_test.cpp" ) foreach(file ${SRCS}) diff --git a/test/tateyama/metrics/metrics_test.cpp b/test/tateyama/metrics/metrics_test.cpp new file mode 100644 index 0000000..bc65ca1 --- /dev/null +++ b/test/tateyama/metrics/metrics_test.cpp @@ -0,0 +1,308 @@ +/* + * Copyright 2022-2024 Project Tsurugi. + * + * 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 "test_root.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include "tateyama/configuration/bootstrap_configuration.h" +#include "tateyama/test_utils/server_mock.h" + +namespace tateyama::test_utils { + +template<> +inline void server_mock::request_message(tateyama::proto::metrics::request::List& rq) { + tateyama::proto::metrics::request::Request r{}; + auto s = current_request(); + EXPECT_TRUE(r.ParseFromString(s)); + EXPECT_EQ(r.command_case(), tateyama::proto::metrics::request::Request::CommandCase::kList); + rq = r.list(); +} + +template<> +inline void server_mock::request_message(tateyama::proto::metrics::request::Show& rq) { + tateyama::proto::metrics::request::Request r{}; + auto s = current_request(); + EXPECT_TRUE(r.ParseFromString(s)); + EXPECT_EQ(r.command_case(), tateyama::proto::metrics::request::Request::CommandCase::kShow); + rq = r.show(); +} + +} // tateyama::test_utils + + +namespace tateyama::metrics { +using namespace std::literals::string_literals; +using metrics_metadata = tateyama::metrics::metrics_metadata; + +class metrics_test : public ::testing::Test { +public: + virtual void SetUp() { + helper_ = std::make_unique("metrics_test", 20301); + helper_->set_up(); + auto bst_conf = tateyama::configuration::bootstrap_configuration::create_bootstrap_configuration(helper_->conf_file_path()); + server_mock_ = std::make_unique("metrics_test", bst_conf.digest(), sync_); + sync_.wait(); + } + + virtual void TearDown() { + helper_->tear_down(); + } + +protected: + std::unique_ptr helper_{}; + std::unique_ptr server_mock_{}; + boost::barrier sync_{2}; + +#if 0 + struct metadata_less { + bool operator()(const tateyama::metrics::metrics_metadata& a, const tateyama::metrics::metrics_metadata& b) const noexcept { + if (a.key() < b.key()) { + return true; + } + auto& a_attrs = a.attributes(); + auto& b_attrs = b.attributes(); + if (a_attrs.size() != a_attrs.size()) { + return a_attrs.size() < b_attrs.size(); + } + for (std::size_t i = 0; i < a_attrs.size(); i++) { + if (a_attrs.at(i) < b_attrs.at(i)) { + return true; + } + } + return false; + } + }; +#endif + std::vector> metadata_value_{ + { + { + "session_count"s, "number of active sessions"s, + std::vector> {}, + std::vector {} + }, + 100 + }, + { + { + "index_size"s, "estimated each index size"s, + std::vector> { + std::tuple {"table_name"s, "A"s}, + std::tuple {"index_name"s, "IA1"} + }, + std::vector {"table_count"s} + }, + 65536 + }, + { + { + "index_size"s, "estimated each index size"s, + std::vector> { + std::tuple {"table_name"s, "A"s}, + std::tuple {"index_name"s, "IA2"} + }, + std::vector {"table_count"s} + }, + 16777216 + }, + { + { + "index_size"s, "estimated each index size"s, + std::vector> { + std::tuple {"table_name"s, "B"s}, + std::tuple {"index_name"s, "IB1"} + }, + std::vector {"table_count"s} + }, + 256 + }, + { + { + "storage_log_size"s, "transaction log disk usage"s, + std::vector> {}, + std::vector {} + }, + 65535 + }, + { + { + "ipc_buffer_size"s, "allocated buffer size for all IPC sessions"s, + std::vector> {}, + std::vector {} + }, + 1024 + }, + { + { + "sql_buffer_size"s, "allocated buffer size for SQL execution engine"s, + std::vector> {}, + std::vector {} + }, + 268435456 + }, + { + { + ""s, ""s, + std::vector> {}, + std::vector {} + }, + 0 + } + }; + + std::string read_pipe(FILE* fp) { + std::stringstream ss{}; + int c{}; + while ((c = std::fgetc(fp)) != EOF) { + ss << static_cast(c); + } + return ss.str(); + } +}; + +TEST_F(metrics_test, metrics_list) { + { + tateyama::proto::metrics::response::MetricsInformation information{}; + + auto size = metadata_value_.size(); + std::string prev_key{}; + for (std::size_t i = 0; i < (size - 1); i++) { + auto&& mev = metadata_value_.at(i); + if (prev_key != mev.first.key()) { + auto* item = information.add_items(); + item->set_key(std::string(mev.first.key())); + item->set_description(std::string(mev.first.description())); + prev_key = mev.first.key(); + } + } + server_mock_->push_response(information.SerializeAsString()); + } + + std::string command; + FILE *fp; + + command = "tgctl dbstats list --format json --conf "; + command += helper_->conf_file_path(); + std::cout << command << std::endl; + if((fp = popen(command.c_str(), "r")) == nullptr){ + std::cerr << "cannot tgctl dbstats list" << std::endl; + } + auto result = read_pipe(fp); +// std::cout << result << std::flush; + EXPECT_TRUE(validate_json_regular(result)); + EXPECT_NE(std::string::npos, result.find("session_count")); + EXPECT_NE(std::string::npos, result.find("number of active sessions")); + EXPECT_NE(std::string::npos, result.find("index_size")); + EXPECT_NE(std::string::npos, result.find("estimated each index size")); + EXPECT_NE(std::string::npos, result.find("storage_log_size")); + EXPECT_NE(std::string::npos, result.find("transaction log disk usage")); + EXPECT_NE(std::string::npos, result.find("ipc_buffer_size")); + EXPECT_NE(std::string::npos, result.find("allocated buffer size for all IPC sessions")); + EXPECT_NE(std::string::npos, result.find("sql_buffer_size")); + EXPECT_NE(std::string::npos, result.find("allocated buffer size for SQL execution engine")); + + EXPECT_EQ(tateyama::framework::service_id_metrics, server_mock_->component_id()); + tateyama::proto::metrics::request::List rq{}; + server_mock_->request_message(rq); +} + +TEST_F(metrics_test, metrics_show) { + { + tateyama::proto::metrics::response::MetricsInformation information{}; + + auto size = metadata_value_.size(); + for (std::size_t i = 0; i < (size - 1); i++) { + auto&& mev = metadata_value_.at(i); + auto* item = information.add_items(); + std::string current_key{mev.first.key()}; + item->set_key(current_key); + item->set_description(std::string(mev.first.description())); + + auto* mv = item->mutable_value(); + if (mev.first.attributes().empty()) { + mv->set_value(mev.second); + } else { + auto* array = mv->mutable_array(); + auto* elements = array->mutable_elements(); + while (true) { + auto* element = elements->Add(); + element->set_value(metadata_value_.at(i).second); + auto& mattrs = metadata_value_.at(i).first.attributes(); + auto* attrmap = element->mutable_attributes(); + for (auto&& e: mattrs) { + (*attrmap)[std::get<0>(e)] = std::get<1>(e); + } + if (current_key != metadata_value_.at(i+1).first.key()) { + break; + } + i++; + } + } + } + server_mock_->push_response(information.SerializeAsString()); + } + + std::string command; + FILE *fp; + + command = "tgctl dbstats show --format json --conf "; + command += helper_->conf_file_path(); + std::cout << command << std::endl; + if((fp = popen(command.c_str(), "r")) == nullptr){ + std::cerr << "cannot tgctl dbstats show" << std::endl; + } + auto result = read_pipe(fp); +// std::cout << result << std::flush; + EXPECT_TRUE(validate_json_regular(result)); + EXPECT_NE(std::string::npos, result.find("session_count")); + EXPECT_NE(std::string::npos, result.find("100")); + + EXPECT_NE(std::string::npos, result.find("index_size")); + EXPECT_NE(std::string::npos, result.find("table_name")); + EXPECT_NE(std::string::npos, result.find("index_name")); + EXPECT_NE(std::string::npos, result.find("value")); + EXPECT_NE(std::string::npos, result.find("A")); + EXPECT_NE(std::string::npos, result.find("IA1")); + EXPECT_NE(std::string::npos, result.find("65536")); + EXPECT_NE(std::string::npos, result.find("IA2")); + EXPECT_NE(std::string::npos, result.find("16777216")); + EXPECT_NE(std::string::npos, result.find("B")); + EXPECT_NE(std::string::npos, result.find("IB1")); + EXPECT_NE(std::string::npos, result.find("256")); + + EXPECT_NE(std::string::npos, result.find("storage_log_size")); + EXPECT_NE(std::string::npos, result.find("65535")); + EXPECT_NE(std::string::npos, result.find("ipc_buffer_size")); + EXPECT_NE(std::string::npos, result.find("1024")); + EXPECT_NE(std::string::npos, result.find("sql_buffer_size")); + EXPECT_NE(std::string::npos, result.find("268435456")); + + EXPECT_EQ(tateyama::framework::service_id_metrics, server_mock_->component_id()); + tateyama::proto::metrics::request::Show rq{}; + server_mock_->request_message(rq); +} + +} // namespace tateyama::metrics diff --git a/test/tateyama/monitor/statistics_mock_test.cpp b/test/tateyama/monitor/statistics_mock_test.cpp deleted file mode 100644 index 879c053..0000000 --- a/test/tateyama/monitor/statistics_mock_test.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2022-2022 Project Tsurugi. - * - * 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 "test_root.h" - -namespace tateyama::testing { - -class statistics_mock_test : public ::testing::Test { -public: - virtual void SetUp() { - helper_ = std::make_unique("statistics_mock_test", 20007); - helper_->set_up(); - } - - virtual void TearDown() { - helper_->tear_down(); - } - -protected: - std::unique_ptr helper_{}; - -private: - std::string name_{}; -}; - -TEST_F(statistics_mock_test, list) { - std::string command; - - command = "tgctl dbstats list --conf "; - command += helper_->conf_file_path(); - command += " --monitor "; - command += helper_->abs_path("test/list.log"); - std::cout << command << std::endl; - if (system(command.c_str()) != 0) { - std::cerr << "cannot tgctl dbstats list" << std::endl; - FAIL(); - } - EXPECT_TRUE(validate_json(helper_->abs_path("test/list.log"))); -} - -TEST_F(statistics_mock_test, show) { - std::string command; - - command = "tgctl dbstats show example-1 --conf "; - command += helper_->conf_file_path(); - command += " --monitor "; - command += helper_->abs_path("test/show.log"); - std::cout << command << std::endl; - if (system(command.c_str()) != 0) { - std::cerr << "cannot tgctl dbstats show" << std::endl; - FAIL(); - } - EXPECT_TRUE(validate_json(helper_->abs_path("test/show.log"))); -} - -} // namespace tateyama::testing diff --git a/test/test_root.h b/test/test_root.h index ec0a5d8..255d083 100644 --- a/test/test_root.h +++ b/test/test_root.h @@ -174,3 +174,18 @@ static bool validate_json(std::filesystem::path file) return result; } + +static bool validate_json_regular(const std::string& str) +{ + bool result = true; + std::stringstream ss; + + ss << str; + try { + boost::property_tree::ptree pt; + boost::property_tree::read_json(ss, pt); + } catch (boost::property_tree::json_parser_error) { + result = false; + } + return result; +}