blob: d2016c0998f5940d4eac73e636b36f4056627214 [file] [log] [blame]
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Microsoft Corporation
*
* -=- Robust Distributed System Nucleus (rDSN) -=-
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "perf_counter/perf_counters.h"
#include <stdio.h>
#include <map>
#include "common/json_helper.h"
#include "gtest/gtest.h"
#include "perf_counter/perf_counter.h"
#include "perf_counter/perf_counter_utils.h"
#include "perf_counter/perf_counter_wrapper.h"
#include "utils/blob.h"
namespace dsn {
TEST(perf_counters_test, counter_create_remove)
{
perf_counter_ptr p;
p = perf_counters::instance().get_global_counter(
"app", "test", "number_counter", COUNTER_TYPE_NUMBER, "", false);
ASSERT_EQ(nullptr, p);
p = perf_counters::instance().get_global_counter(
"app", "test", "number_counter", COUNTER_TYPE_NUMBER, "", true);
ASSERT_NE(nullptr, p);
p = perf_counters::instance().get_global_counter(
"app", "test", "number_counter", COUNTER_TYPE_NUMBER, "", false);
ASSERT_NE(nullptr, p);
p = perf_counters::instance().get_global_counter(
"app", "test", "volatile_number_counter", COUNTER_TYPE_VOLATILE_NUMBER, "", false);
ASSERT_EQ(nullptr, p);
p = perf_counters::instance().get_global_counter(
"app", "test", "volatile_number_counter", COUNTER_TYPE_VOLATILE_NUMBER, "", true);
ASSERT_NE(nullptr, p);
p = perf_counters::instance().get_global_counter(
"app", "test", "volatile_number_counter", COUNTER_TYPE_VOLATILE_NUMBER, "", false);
ASSERT_NE(nullptr, p);
p = perf_counters::instance().get_global_counter(
"app", "test", "rate_counter", COUNTER_TYPE_RATE, "", false);
ASSERT_EQ(nullptr, p);
p = perf_counters::instance().get_global_counter(
"app", "test", "rate_counter", COUNTER_TYPE_RATE, "", true);
ASSERT_NE(nullptr, p);
p = perf_counters::instance().get_global_counter(
"app", "test", "rate_counter", COUNTER_TYPE_RATE, "", false);
ASSERT_NE(nullptr, p);
ASSERT_FALSE(perf_counters::instance().remove_counter("number_counter"));
ASSERT_FALSE(perf_counters::instance().remove_counter("unexist_counter"));
ASSERT_TRUE(perf_counters::instance().remove_counter("app*test*number_counter"));
ASSERT_TRUE(perf_counters::instance().remove_counter("app*test*number_counter"));
ASSERT_FALSE(perf_counters::instance().remove_counter("app*test*number_counter"));
p = perf_counters::instance().get_global_counter(
"app", "test", "number_counter", COUNTER_TYPE_NUMBER, "", false);
ASSERT_EQ(nullptr, p);
ASSERT_TRUE(perf_counters::instance().remove_counter("app*test*volatile_number_counter"));
ASSERT_TRUE(perf_counters::instance().remove_counter("app*test*volatile_number_counter"));
ASSERT_FALSE(perf_counters::instance().remove_counter("app*test*volatile_number_counter"));
p = perf_counters::instance().get_global_counter(
"app", "test", "volatile_number_counter", COUNTER_TYPE_VOLATILE_NUMBER, "", false);
ASSERT_EQ(nullptr, p);
ASSERT_TRUE(perf_counters::instance().remove_counter("app*test*rate_counter"));
ASSERT_TRUE(perf_counters::instance().remove_counter("app*test*rate_counter"));
ASSERT_FALSE(perf_counters::instance().remove_counter("app*test*rate_counter"));
p = perf_counters::instance().get_global_counter(
"app", "test", "rate_counter", COUNTER_TYPE_RATE, "", false);
ASSERT_EQ(nullptr, p);
p = perf_counters::instance().get_global_counter(
"app", "test", "unexist_counter", COUNTER_TYPE_NUMBER, "", false);
ASSERT_EQ(nullptr, p);
ASSERT_FALSE(perf_counters::instance().remove_counter("app*test*unexist_counter"));
}
template <typename K, typename V>
bool check_map_contains(const std::map<K, V> &super, const std::map<K, V> &sub)
{
for (const auto &kv : sub) {
auto it = super.find(kv.first);
if (it == super.end()) {
return false;
}
if (it->second != kv.second) {
return false;
}
}
return true;
}
TEST(perf_counters_test, snapshot)
{
std::map<std::string, dsn_perf_counter_type_t> expected;
std::map<std::string, dsn_perf_counter_type_t> counter_keys;
perf_counters::instance().take_snapshot();
perf_counters::snapshot_iterator iter =
[&counter_keys](const perf_counters::counter_snapshot &cs) mutable {
counter_keys.emplace(cs.name, cs.type);
};
counter_keys.clear();
expected = {
{"replica*server*memused.virt(MB)", COUNTER_TYPE_NUMBER},
{"replica*server*memused.res(MB)", COUNTER_TYPE_NUMBER},
};
perf_counters::instance().iterate_snapshot(iter);
// in the beginning, builtin counters are in counter_list
ASSERT_TRUE(check_map_contains(counter_keys, expected));
dsn::perf_counter_wrapper c1;
c1.init_global_counter("a", "s", "test_counter", COUNTER_TYPE_NUMBER, "");
dsn::perf_counter_wrapper c2;
c2.init_global_counter("a", "s", "test_counter", COUNTER_TYPE_NUMBER, "");
dsn::perf_counter_wrapper c3;
c3.init_global_counter("b", "s", "test_counter", COUNTER_TYPE_VOLATILE_NUMBER, "");
dsn::perf_counter_wrapper c4;
c4.init_global_counter("b", "s", "test_counter", COUNTER_TYPE_VOLATILE_NUMBER, "");
// snapshot will contain new counters
perf_counters::instance().take_snapshot();
counter_keys.clear();
expected = {
{"replica*server*memused.virt(MB)", COUNTER_TYPE_NUMBER},
{"replica*server*memused.res(MB)", COUNTER_TYPE_NUMBER},
{"a*s*test_counter", COUNTER_TYPE_NUMBER},
{"b*s*test_counter", COUNTER_TYPE_VOLATILE_NUMBER},
};
perf_counters::instance().iterate_snapshot(iter);
ASSERT_TRUE(check_map_contains(counter_keys, expected));
dsn::perf_counter_wrapper c5;
c5.init_global_counter("c", "s", "test_counter", COUNTER_TYPE_RATE, "");
dsn::perf_counter_wrapper c6;
c6.init_global_counter("c", "s", "test_counter", COUNTER_TYPE_RATE, "");
dsn::perf_counter_wrapper c7;
c7.init_global_counter("d", "s", "test_counter", COUNTER_TYPE_NUMBER_PERCENTILES, "");
dsn::perf_counter_wrapper c8;
c8.init_global_counter("d", "s", "test_counter", COUNTER_TYPE_NUMBER_PERCENTILES, "");
// new counters won't be contained in snapshot if you don't call "take snapshot"
counter_keys.clear();
expected = {
{"replica*server*memused.virt(MB)", COUNTER_TYPE_NUMBER},
{"replica*server*memused.res(MB)", COUNTER_TYPE_NUMBER},
{"a*s*test_counter", COUNTER_TYPE_NUMBER},
{"b*s*test_counter", COUNTER_TYPE_VOLATILE_NUMBER},
};
perf_counters::instance().iterate_snapshot(iter);
ASSERT_TRUE(check_map_contains(counter_keys, expected));
ASSERT_TRUE(counter_keys.find("c*s*test_counter") == counter_keys.end());
ASSERT_TRUE(counter_keys.find("d*s*test_counter") == counter_keys.end());
// after taking snapshot, new counters will be contained
counter_keys.clear();
expected = {
{"replica*server*memused.virt(MB)", COUNTER_TYPE_NUMBER},
{"replica*server*memused.res(MB)", COUNTER_TYPE_NUMBER},
{"a*s*test_counter", COUNTER_TYPE_NUMBER},
{"b*s*test_counter", COUNTER_TYPE_VOLATILE_NUMBER},
{"c*s*test_counter", COUNTER_TYPE_RATE},
{"d*s*test_counter", COUNTER_TYPE_NUMBER_PERCENTILES},
};
perf_counters::instance().take_snapshot();
perf_counters::instance().iterate_snapshot(iter);
ASSERT_TRUE(check_map_contains(counter_keys, expected));
c1.clear();
c2.clear();
c3.clear();
c4.clear();
// although remove counters, but snapshot won't been affected if you don't call take snapshot
counter_keys.clear();
perf_counters::instance().iterate_snapshot(iter);
expected = {
{"replica*server*memused.virt(MB)", COUNTER_TYPE_NUMBER},
{"replica*server*memused.res(MB)", COUNTER_TYPE_NUMBER},
{"a*s*test_counter", COUNTER_TYPE_NUMBER},
{"b*s*test_counter", COUNTER_TYPE_VOLATILE_NUMBER},
{"c*s*test_counter", COUNTER_TYPE_RATE},
{"d*s*test_counter", COUNTER_TYPE_NUMBER_PERCENTILES},
};
ASSERT_TRUE(check_map_contains(counter_keys, expected));
// after take snapshot, removed counters will be removed in snapshot
perf_counters::instance().take_snapshot();
counter_keys.clear();
perf_counters::instance().iterate_snapshot(iter);
expected = {
{"replica*server*memused.virt(MB)", COUNTER_TYPE_NUMBER},
{"replica*server*memused.res(MB)", COUNTER_TYPE_NUMBER},
{"c*s*test_counter", COUNTER_TYPE_RATE},
{"d*s*test_counter", COUNTER_TYPE_NUMBER_PERCENTILES},
};
ASSERT_TRUE(check_map_contains(counter_keys, expected));
ASSERT_TRUE(counter_keys.find("a*s*test_counter") == counter_keys.end());
ASSERT_TRUE(counter_keys.find("b*s*test_counter") == counter_keys.end());
// query snapshot
std::vector<std::string> target_keys = {
"a*s*test_counter", "c*s*test_counter", "b*s*test_counter", "d*s*test_counter"};
expected = {
{"c*s*test_counter", COUNTER_TYPE_RATE},
{"d*s*test_counter", COUNTER_TYPE_NUMBER_PERCENTILES},
};
counter_keys.clear();
perf_counters::instance().query_snapshot(target_keys, iter, nullptr);
ASSERT_EQ(2, counter_keys.size());
ASSERT_EQ(expected, counter_keys);
counter_keys.clear();
std::vector<bool> found;
perf_counters::instance().query_snapshot(target_keys, iter, &found);
ASSERT_EQ(4, found.size());
std::vector<bool> expected_found = {false, true, false, true};
ASSERT_EQ(expected_found, found);
ASSERT_EQ(expected, counter_keys);
}
TEST(perf_counters_test, query_snapshot_by_regexp)
{
dsn::perf_counter_wrapper c1;
c1.init_global_counter("a", "s", "test_counter", COUNTER_TYPE_NUMBER, "");
dsn::perf_counter_wrapper c2;
c2.init_global_counter("a", "s", "test_counter", COUNTER_TYPE_NUMBER, "");
dsn::perf_counter_wrapper c3;
c3.init_global_counter("b", "s", "test_counter", COUNTER_TYPE_VOLATILE_NUMBER, "");
dsn::perf_counter_wrapper c4;
c4.init_global_counter("b", "s", "test_counter", COUNTER_TYPE_VOLATILE_NUMBER, "");
dsn::perf_counter_wrapper c5;
c5.init_global_counter("c", "s", "test_counter", COUNTER_TYPE_RATE, "");
dsn::perf_counter_wrapper c6;
c6.init_global_counter("c", "s", "test_counter", COUNTER_TYPE_RATE, "");
dsn::perf_counter_wrapper c7;
c7.init_global_counter("d", "s", "test_counter", COUNTER_TYPE_NUMBER_PERCENTILES, "");
dsn::perf_counter_wrapper c8;
c8.init_global_counter("d", "s", "test_counter", COUNTER_TYPE_NUMBER_PERCENTILES, "");
perf_counters::instance().take_snapshot();
std::string result = perf_counters::instance().list_snapshot_by_regexp({".*\\*s\\*.*"});
dsn::perf_counter_info info;
dsn::json::json_forwarder<dsn::perf_counter_info>::decode(
dsn::blob(result.c_str(), 0, result.size()), info);
ASSERT_STREQ("OK", info.result.c_str());
ASSERT_GT(info.timestamp, 0);
ASSERT_TRUE(!info.timestamp_str.empty());
printf("got timestamp: %s\n", info.timestamp_str.c_str());
ASSERT_EQ(4 + 1, info.counters.size()); // add 1 for p999 counter
std::map<std::string, std::string> expected = {
{"a*s*test_counter", dsn_counter_type_to_string(COUNTER_TYPE_NUMBER)},
{"b*s*test_counter", dsn_counter_type_to_string(COUNTER_TYPE_VOLATILE_NUMBER)},
{"c*s*test_counter", dsn_counter_type_to_string(COUNTER_TYPE_RATE)},
{"d*s*test_counter", dsn_counter_type_to_string(COUNTER_TYPE_NUMBER_PERCENTILES)},
{"d*s*test_counter.p999", dsn_counter_type_to_string(COUNTER_TYPE_NUMBER_PERCENTILES)},
};
std::map<std::string, std::string> actual;
for (const dsn::perf_counter_metric &m : info.counters) {
actual.emplace(m.name, m.type);
}
ASSERT_EQ(expected, actual);
result = perf_counters::instance().list_snapshot_by_regexp({"hahaha"});
dsn::json::json_forwarder<dsn::perf_counter_info>::decode(
dsn::blob(result.c_str(), 0, result.size()), info);
ASSERT_STREQ("OK", info.result.c_str());
ASSERT_GT(info.timestamp, 0);
ASSERT_TRUE(!info.timestamp_str.empty());
printf("got timestamp: %s\n", info.timestamp_str.c_str());
ASSERT_TRUE(info.counters.empty());
result = perf_counters::instance().list_snapshot_by_regexp({""});
dsn::json::json_forwarder<dsn::perf_counter_info>::decode(
dsn::blob(result.c_str(), 0, result.size()), info);
ASSERT_STREQ("OK", info.result.c_str());
ASSERT_GT(info.timestamp, 0);
ASSERT_TRUE(!info.timestamp_str.empty());
printf("got timestamp: %s\n", info.timestamp_str.c_str());
ASSERT_TRUE(info.counters.empty());
}
} // namespace dsn