Files
doris/be/test/util/new_metrics_test.cpp
Yingchun Lai e71152132c [metrics] Redesign metrics to 3 layers (#4115)
Redesign metrics to 3 layers:
    MetricRegistry - MetricEntity - Metrics
    MetricRegistry : the register center
    MetricEntity : the entity registered on MetricRegistry. Generally a MetricRegistry can be registered on several 
        MetricEntities, each of MetricEntity is an independent entity, such as server, disk_devices, data_directories, thrift 
        clients and servers, and so on. 
    Metric : metrics of an entity. Such as fragment_requests_total on server entity, disk_bytes_read on a disk_device entity, 
        thrift_opened_clients on a thrift_client entity.
    MetricPrototype: the type of a metric. MetricPrototype is a global variable, can be shared by the same metrics across 
        different MetricEntities.
2020-08-08 11:23:01 +08:00

400 lines
13 KiB
C++

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 <gtest/gtest.h>
#include <iostream>
#include <thread>
#include "common/config.h"
#include "util/logging.h"
#include "util/metrics.h"
#include "util/stopwatch.hpp"
namespace doris {
class MetricsTest : public testing::Test {
public:
MetricsTest() { }
virtual ~MetricsTest() {
}
};
TEST_F(MetricsTest, Counter) {
{
IntCounter counter;
ASSERT_EQ(0, counter.value());
counter.increment(100);
ASSERT_EQ(100, counter.value());
ASSERT_STREQ("100", counter.to_string().c_str());
}
{
IntAtomicCounter counter;
ASSERT_EQ(0, counter.value());
counter.increment(100);
ASSERT_EQ(100, counter.value());
ASSERT_STREQ("100", counter.to_string().c_str());
}
{
UIntCounter counter;
ASSERT_EQ(0, counter.value());
counter.increment(100);
ASSERT_EQ(100, counter.value());
ASSERT_STREQ("100", counter.to_string().c_str());
}
{
DoubleCounter counter;
ASSERT_EQ(0, counter.value());
counter.increment(1.23);
ASSERT_EQ(1.23, counter.value());
ASSERT_STREQ("1.230000", counter.to_string().c_str());
}
}
template<typename T>
void mt_updater(T* counter, std::atomic<uint64_t>* used_time) {
sleep(1);
MonotonicStopWatch watch;
watch.start();
for (int i = 0; i < 1000000L; ++i) {
counter->increment(1);
}
uint64_t elapsed = watch.elapsed_time();
used_time->fetch_add(elapsed);
}
TEST_F(MetricsTest, CounterPerf) {
static const int kLoopCount = 100000000;
// volatile int64_t
{
volatile int64_t sum = 0;
MonotonicStopWatch watch;
watch.start();
for (int i = 0; i < kLoopCount; ++i) {
sum += 1;
}
uint64_t elapsed = watch.elapsed_time();
ASSERT_EQ(kLoopCount, sum);
LOG(INFO) << "int64_t: elapsed: " << elapsed
<< "ns, ns/iter:" << elapsed / kLoopCount;
}
// IntAtomicCounter
{
IntAtomicCounter counter;
MonotonicStopWatch watch;
watch.start();
for (int i = 0; i < kLoopCount; ++i) {
counter.increment(1);
}
uint64_t elapsed = watch.elapsed_time();
ASSERT_EQ(kLoopCount, counter.value());
LOG(INFO) << "IntAtomicCounter: elapsed: " << elapsed
<< "ns, ns/iter:" << elapsed / kLoopCount;
}
// IntCounter
{
IntCounter counter;
MonotonicStopWatch watch;
watch.start();
for (int i = 0; i < kLoopCount; ++i) {
counter.increment(1);
}
uint64_t elapsed = watch.elapsed_time();
ASSERT_EQ(kLoopCount, counter.value());
LOG(INFO) << "IntCounter: elapsed: " << elapsed
<< "ns, ns/iter:" << elapsed / kLoopCount;
}
// multi-thread for IntCounter
{
IntCounter mt_counter;
std::vector<std::thread> updaters;
std::atomic<uint64_t> used_time(0);
for (int i = 0; i < 8; ++i) {
updaters.emplace_back(&mt_updater<IntCounter>, &mt_counter, &used_time);
}
for (int i = 0; i < 8; ++i) {
updaters[i].join();
}
LOG(INFO) << "IntCounter multi-thread elapsed: " << used_time.load()
<< "ns, ns/iter:" << used_time.load() / (8 * 1000000L);
ASSERT_EQ(8 * 1000000L, mt_counter.value());
}
// multi-thread for IntAtomicCounter
{
IntAtomicCounter mt_counter;
std::vector<std::thread> updaters;
std::atomic<uint64_t> used_time(0);
for (int i = 0; i < 8; ++i) {
updaters.emplace_back(&mt_updater<IntAtomicCounter>, &mt_counter, &used_time);
}
for (int i = 0; i < 8; ++i) {
updaters[i].join();
}
LOG(INFO) << "IntAtomicCounter multi-thread elapsed: " << used_time.load()
<< "ns, ns/iter:" << used_time.load() / (8 * 1000000L);
ASSERT_EQ(8 * 1000000L, mt_counter.value());
}
}
TEST_F(MetricsTest, Gauge) {
// IntGauge
{
IntGauge gauge;
ASSERT_EQ(0, gauge.value());
gauge.set_value(100);
ASSERT_EQ(100, gauge.value());
ASSERT_STREQ("100", gauge.to_string().c_str());
}
// UIntGauge
{
UIntGauge gauge;
ASSERT_EQ(0, gauge.value());
gauge.set_value(100);
ASSERT_EQ(100, gauge.value());
ASSERT_STREQ("100", gauge.to_string().c_str());
}
// DoubleGauge
{
DoubleGauge gauge;
ASSERT_EQ(0.0, gauge.value());
gauge.set_value(1.23);
ASSERT_EQ(1.23, gauge.value());
ASSERT_STREQ("1.230000", gauge.to_string().c_str());
}
}
TEST_F(MetricsTest, MetricPrototype) {
{
MetricPrototype cpu_idle_type(MetricType::COUNTER, MetricUnit::PERCENT, "fragment_requests_total",
"Total fragment requests received.");
ASSERT_EQ("fragment_requests_total", cpu_idle_type.simple_name());
ASSERT_EQ("fragment_requests_total", cpu_idle_type.combine_name(""));
ASSERT_EQ("doris_be_fragment_requests_total", cpu_idle_type.combine_name("doris_be"));
}
{
MetricPrototype cpu_idle_type(MetricType::COUNTER, MetricUnit::PERCENT, "cpu_idle",
"CPU's idle time percent", "cpu");
ASSERT_EQ("cpu", cpu_idle_type.simple_name());
ASSERT_EQ("cpu", cpu_idle_type.combine_name(""));
ASSERT_EQ("doris_be_cpu", cpu_idle_type.combine_name("doris_be"));
}
}
TEST_F(MetricsTest, MetricEntityWithMetric) {
MetricEntity entity("test_entity", {});
IntCounter cpu_idle;
MetricPrototype cpu_idle_type(MetricType::COUNTER, MetricUnit::PERCENT, "cpu_idle");
// Before register
Metric* metric = entity.get_metric("cpu_idle");
ASSERT_EQ(nullptr, metric);
// Register
entity.register_metric(&cpu_idle_type, &cpu_idle);
cpu_idle.increment(12);
metric = entity.get_metric("cpu_idle");
ASSERT_NE(nullptr, metric);
ASSERT_EQ("12", metric->to_string());
cpu_idle.increment(8);
ASSERT_EQ("20", metric->to_string());
// Deregister
entity.deregister_metric(&cpu_idle_type);
// After deregister
metric = entity.get_metric("cpu_idle");
ASSERT_EQ(nullptr, metric);
}
TEST_F(MetricsTest, MetricEntityWithHook) {
MetricEntity entity("test_entity", {});
IntCounter cpu_idle;
MetricPrototype cpu_idle_type(MetricType::COUNTER, MetricUnit::PERCENT, "cpu_idle");
// Register
entity.register_metric(&cpu_idle_type, &cpu_idle);
entity.register_hook("test_hook", [&cpu_idle]() {
cpu_idle.increment(6);
});
// Before hook
Metric* metric = entity.get_metric("cpu_idle");
ASSERT_NE(nullptr, metric);
ASSERT_EQ("0", metric->to_string());
// Hook
entity.trigger_hook_unlocked(true);
ASSERT_EQ("6", metric->to_string());
entity.trigger_hook_unlocked(true);
ASSERT_EQ("12", metric->to_string());
// Deregister hook
entity.deregister_hook("test_hook");
// Hook but no effect
entity.trigger_hook_unlocked(true);
ASSERT_EQ("12", metric->to_string());
}
TEST_F(MetricsTest, MetricRegistryRegister) {
MetricRegistry registry("test_registry");
// No entity
ASSERT_EQ("", registry.to_prometheus());
ASSERT_EQ("[]", registry.to_json());
ASSERT_EQ("", registry.to_core_string());
// Before register
auto entity = registry.get_entity("test_entity").get();
ASSERT_EQ(nullptr, entity);
// Register
entity = registry.register_entity("test_entity", {});
ASSERT_NE(nullptr, entity);
// After register
auto entity1 = registry.get_entity("test_entity").get();
ASSERT_NE(nullptr, entity1);
ASSERT_EQ(entity, entity1);
registry.deregister_entity("test_entity");
entity = registry.get_entity("test_entity").get();
ASSERT_EQ(nullptr, entity);
}
TEST_F(MetricsTest, MetricRegistryOutput) {
MetricRegistry registry("test_registry");
{
// No entity
ASSERT_EQ("", registry.to_prometheus());
ASSERT_EQ("[]", registry.to_json());
ASSERT_EQ("", registry.to_core_string());
}
{
// Register one common metric to the entity
auto entity = registry.register_entity("test_entity", {});
IntGauge cpu_idle;
MetricPrototype cpu_idle_type(MetricType::GAUGE, MetricUnit::PERCENT, "cpu_idle", "", "", {}, true);
entity->register_metric(&cpu_idle_type, &cpu_idle);
cpu_idle.increment(8);
ASSERT_EQ(R"(# TYPE test_registry_cpu_idle gauge
test_registry_cpu_idle 8
)", registry.to_prometheus());
ASSERT_EQ(R"([{"tags":{"metric":"cpu_idle"},"unit":"percent","value":8}])", registry.to_json());
ASSERT_EQ("test_registry_cpu_idle LONG 8\n", registry.to_core_string());
registry.deregister_entity("test_entity");
}
{
// Register one metric with group name to the entity
auto entity = registry.register_entity("test_entity", {});
IntGauge cpu_idle;
MetricPrototype cpu_idle_type(MetricType::GAUGE, MetricUnit::PERCENT, "cpu_idle", "", "cpu", {{"mode", "idle"}}, false);
entity->register_metric(&cpu_idle_type, &cpu_idle);
cpu_idle.increment(18);
ASSERT_EQ(R"(# TYPE test_registry_cpu gauge
test_registry_cpu{mode="idle"} 18
)", registry.to_prometheus());
ASSERT_EQ(R"([{"tags":{"metric":"cpu","mode":"idle"},"unit":"percent","value":18}])", registry.to_json());
ASSERT_EQ("", registry.to_core_string());
registry.deregister_entity("test_entity");
}
{
// Register one common metric to an entity with label
auto entity = registry.register_entity("test_entity", {{"name", "lable_test"}});
IntGauge cpu_idle;
MetricPrototype cpu_idle_type(MetricType::GAUGE, MetricUnit::PERCENT, "cpu_idle");
entity->register_metric(&cpu_idle_type, &cpu_idle);
cpu_idle.increment(28);
ASSERT_EQ(R"(# TYPE test_registry_cpu_idle gauge
test_registry_cpu_idle{name="lable_test"} 28
)", registry.to_prometheus());
ASSERT_EQ(R"([{"tags":{"metric":"cpu_idle","name":"lable_test"},"unit":"percent","value":28}])", registry.to_json());
ASSERT_EQ("", registry.to_core_string());
registry.deregister_entity("test_entity");
}
{
// Register one common metric with group name to an entity with label
auto entity = registry.register_entity("test_entity", {{"name", "lable_test"}});
IntGauge cpu_idle;
MetricPrototype cpu_idle_type(MetricType::GAUGE, MetricUnit::PERCENT, "cpu_idle", "", "cpu", {{"mode", "idle"}});
entity->register_metric(&cpu_idle_type, &cpu_idle);
cpu_idle.increment(38);
ASSERT_EQ(R"(# TYPE test_registry_cpu gauge
test_registry_cpu{name="lable_test",mode="idle"} 38
)", registry.to_prometheus());
ASSERT_EQ(R"([{"tags":{"metric":"cpu","mode":"idle","name":"lable_test"},"unit":"percent","value":38}])", registry.to_json());
ASSERT_EQ("", registry.to_core_string());
registry.deregister_entity("test_entity");
}
{
// Register two common metrics to one entity
auto entity = registry.register_entity("test_entity", {});
IntGauge cpu_idle;
MetricPrototype cpu_idle_type(MetricType::GAUGE, MetricUnit::PERCENT, "cpu_idle", "", "cpu", {{"mode", "idle"}});
entity->register_metric(&cpu_idle_type, &cpu_idle);
cpu_idle.increment(48);
IntGauge cpu_guest;
MetricPrototype cpu_guest_type(MetricType::GAUGE, MetricUnit::PERCENT, "cpu_guest", "", "cpu", {{"mode", "guest"}});
entity->register_metric(&cpu_guest_type, &cpu_guest);
cpu_guest.increment(58);
ASSERT_EQ(R"(# TYPE test_registry_cpu gauge
test_registry_cpu{mode="idle"} 48
test_registry_cpu{mode="guest"} 58
)", registry.to_prometheus());
ASSERT_EQ(R"([{"tags":{"metric":"cpu","mode":"guest"},"unit":"percent","value":58},{"tags":{"metric":"cpu","mode":"idle"},"unit":"percent","value":48}])", registry.to_json());
ASSERT_EQ("", registry.to_core_string());
registry.deregister_entity("test_entity");
}
}
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}