diff options
Diffstat (limited to 'src/seastar/tests/unit/metrics_test.cc')
-rw-r--r-- | src/seastar/tests/unit/metrics_test.cc | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/src/seastar/tests/unit/metrics_test.cc b/src/seastar/tests/unit/metrics_test.cc new file mode 100644 index 000000000..4dde007d3 --- /dev/null +++ b/src/seastar/tests/unit/metrics_test.cc @@ -0,0 +1,319 @@ +/* + * This file is open source software, licensed to you under the terms + * of the Apache License, Version 2.0 (the "License"). See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. 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. + */ + +/* + * Copyright (C) 2019 ScyllaDB. + */ + +#include <seastar/core/metrics_registration.hh> +#include <seastar/core/metrics.hh> +#include <seastar/core/metrics_api.hh> +#include <seastar/core/relabel_config.hh> +#include <seastar/core/reactor.hh> +#include <seastar/core/scheduling.hh> +#include <seastar/core/sleep.hh> +#include <seastar/core/sharded.hh> +#include <seastar/core/do_with.hh> +#include <seastar/core/io_queue.hh> +#include <seastar/core/loop.hh> +#include <seastar/testing/test_case.hh> +#include <seastar/testing/thread_test_case.hh> +#include <seastar/testing/test_runner.hh> +#include <boost/range/irange.hpp> + +SEASTAR_TEST_CASE(test_add_group) { + using namespace seastar::metrics; + // Just has to compile: + metric_groups() + .add_group("g1", {}) + .add_group("g2", std::vector<metric_definition>()); + return seastar::make_ready_future(); +} + +/** + * This function return the different name label values + * for the named metric. + * + * @note: If the statistic or label doesn't exist, the test + * that calls this function will fail. + * + * @param metric_name - the metric name + * @param label_name - the label name + * @return a set containing all the different values + * of the label. + */ +static std::set<seastar::sstring> get_label_values(seastar::sstring metric_name, seastar::sstring label_name) { + namespace smi = seastar::metrics::impl; + auto all_metrics = smi::get_values(); + const auto& all_metadata = *all_metrics->metadata; + const auto qp_group = find_if(cbegin(all_metadata), cend(all_metadata), + [&metric_name] (const auto& x) { return x.mf.name == metric_name; }); + BOOST_REQUIRE(qp_group != cend(all_metadata)); + std::set<seastar::sstring> labels; + for (const auto& metric : qp_group->metrics) { + const auto found = metric.id.labels().find(label_name); + BOOST_REQUIRE(found != metric.id.labels().cend()); + labels.insert(found->second); + } + return labels; +} + +SEASTAR_THREAD_TEST_CASE(test_renaming_scheuling_groups) { + // this seams a little bit out of place but the + // renaming functionality is primarily for statistics + // otherwise those classes could have just been reused + // without renaming them. + using namespace seastar; + + static const char* name1 = "A"; + static const char* name2 = "B"; + scheduling_group sg = create_scheduling_group("hello", 111).get0(); + boost::integer_range<int> rng(0, 1000); + // repeatedly change the group name back and forth in + // decresing time intervals to see if it generate double + //registration statistics errors. + for (auto&& i : rng) { + const char* name = i%2 ? name1 : name2; + const char* prev_name = i%2 ? name2 : name1; + sleep(std::chrono::microseconds(100000/(i+1))).get(); + rename_scheduling_group(sg, name).get(); + std::set<sstring> label_vals = get_label_values(sstring("scheduler_shares"), sstring("group")); + // validate that the name that we *renamed to* is in the stats + BOOST_REQUIRE(label_vals.find(sstring(name)) != label_vals.end()); + // validate that the name that we *renamed from* is *not* in the stats + BOOST_REQUIRE(label_vals.find(sstring(prev_name)) == label_vals.end()); + } + + smp::invoke_on_all([sg] () { + return do_with(std::uniform_int_distribution<int>(), boost::irange<int>(0, 1000), + [sg] (std::uniform_int_distribution<int>& dist, boost::integer_range<int>& rng) { + // flip a fair coin and rename to one of two options and rename to that + // scheduling group name, do it 1000 in parallel on all shards so there + // is a chance of collision. + return do_for_each(rng, [sg, &dist] (auto i) { + bool odd = dist(seastar::testing::local_random_engine)%2; + return rename_scheduling_group(sg, odd ? name1 : name2); + }); + }); + }).get(); + + std::set<sstring> label_vals = get_label_values(sstring("scheduler_shares"), sstring("group")); + // validate that only one of the names is eventually in the metrics + bool name1_found = label_vals.find(sstring(name1)) != label_vals.end(); + bool name2_found = label_vals.find(sstring(name2)) != label_vals.end(); + BOOST_REQUIRE((name1_found && !name2_found) || (name2_found && !name1_found)); +} + +SEASTAR_THREAD_TEST_CASE(test_renaming_io_priority_classes) { + // this seams a little bit out of place but the + // renaming functionality is primarily for statistics + // otherwise those classes could have just been reused + // without renaming them. + using namespace seastar; + static const char* name1 = "A"; + static const char* name2 = "B"; + seastar::io_priority_class pc = io_priority_class::register_one("hello",100); + smp::invoke_on_all([&pc] () { + // this is a trick to get all of the queues actually register their + // stats. + return pc.update_shares(101); + }).get(); + + boost::integer_range<int> rng(0, 1000); + // repeatedly change the group name back and forth in + // decresing time intervals to see if it generate double + //registration statistics errors. + for (auto&& i : rng) { + const char* name = i%2 ? name1 : name2; + const char* prev_name = i%2 ? name2 : name1; + sleep(std::chrono::microseconds(100000/(i+1))).get(); + pc.rename(name).get(); + std::set<sstring> label_vals = get_label_values(sstring("io_queue_shares"), sstring("class")); + // validate that the name that we *renamed to* is in the stats + BOOST_REQUIRE(label_vals.find(sstring(name)) != label_vals.end()); + // validate that the name that we *renamed from* is *not* in the stats + BOOST_REQUIRE(label_vals.find(sstring(prev_name)) == label_vals.end()); + } + + smp::invoke_on_all([&pc] () { + return do_with(std::uniform_int_distribution<int>(), boost::irange<int>(0, 1000), + [&pc] (std::uniform_int_distribution<int>& dist, boost::integer_range<int>& rng) { + // flip a fair coin and rename to one of two options and rename to that + // scheduling group name, do it 1000 in parallel on all shards so there + // is a chance of collision. + return do_for_each(rng, [&pc, &dist] (auto i) { + bool odd = dist(seastar::testing::local_random_engine)%2; + return pc.rename(odd ? name1 : name2); + }); + }); + }).get(); + + std::set<sstring> label_vals = get_label_values(sstring("io_queue_shares"), sstring("class")); + // validate that only one of the names is eventually in the metrics + bool name1_found = label_vals.find(sstring(name1)) != label_vals.end(); + bool name2_found = label_vals.find(sstring(name2)) != label_vals.end(); + BOOST_REQUIRE((name1_found && !name2_found) || (name2_found && !name1_found)); +} + +int count_by_label(const std::string& label) { + seastar::foreign_ptr<seastar::metrics::impl::values_reference> values = seastar::metrics::impl::get_values(); + int count = 0; + for (auto&& md : (*values->metadata)) { + for (auto&& mi : md.metrics) { + if (label == "" || mi.id.labels().find(label) != mi.id.labels().end()) { + count++; + } + } + } + return count; +} + +int count_by_fun(std::function<bool(const seastar::metrics::impl::metric_info&)> f) { + seastar::foreign_ptr<seastar::metrics::impl::values_reference> values = seastar::metrics::impl::get_values(); + int count = 0; + for (auto&& md : (*values->metadata)) { + for (auto&& mi : md.metrics) { + if (f(mi)) { + count++; + } + } + } + return count; +} + +SEASTAR_THREAD_TEST_CASE(test_relabel_add_labels) { + using namespace seastar::metrics; + namespace sm = seastar::metrics; + sm::metric_groups app_metrics; + app_metrics.add_group("test", { + sm::make_gauge("gauge_1", sm::description("gague 1"), [] { return 0; }), + sm::make_counter("counter_1", sm::description("counter 1"), [] { return 1; }) + }); + + std::vector<sm::relabel_config> rl(1); + rl[0].source_labels = {"__name__"}; + rl[0].target_label = "level"; + rl[0].replacement = "1"; + rl[0].expr = "test_counter_.*"; + + sm::metric_relabeling_result success = sm::set_relabel_configs(rl).get(); + BOOST_CHECK_EQUAL(success.metrics_relabeled_due_to_collision, 0); + BOOST_CHECK_EQUAL(count_by_label("level"), 1); + app_metrics.add_group("test", { + sm::make_counter("counter_2", sm::description("counter 2"), [] { return 2; }) + }); + BOOST_CHECK_EQUAL(count_by_label("level"), 2); + sm::set_relabel_configs({}).get(); +} + +SEASTAR_THREAD_TEST_CASE(test_relabel_drop_label_prevent_runtime_conflicts) { + using namespace seastar::metrics; + namespace sm = seastar::metrics; + sm::metric_groups app_metrics; + app_metrics.add_group("test2", { + sm::make_gauge("gauge_1", sm::description("gague 1"), { sm::label_instance("g", "1")}, [] { return 0; }), + sm::make_counter("counter_1", sm::description("counter 1"), [] { return 0; }), + sm::make_counter("counter_1", sm::description("counter 1"), { sm::label_instance("lev", "2")}, [] { return 0; }) + }); + BOOST_CHECK_EQUAL(count_by_label("lev"), 1); + + std::vector<sm::relabel_config> rl(1); + rl[0].source_labels = {"lev"}; + rl[0].expr = "2"; + rl[0].target_label = "lev"; + rl[0].action = sm::relabel_config::relabel_action::drop_label; + // Dropping the lev label would cause a conflict, but not crash the system + sm::metric_relabeling_result success = sm::set_relabel_configs(rl).get(); + BOOST_CHECK_EQUAL(success.metrics_relabeled_due_to_collision, 1); + BOOST_CHECK_EQUAL(count_by_label("lev"), 0); + BOOST_CHECK_EQUAL(count_by_label("err"), 1); + + //reseting all the labels to their original state + success = sm::set_relabel_configs({}).get(); + BOOST_CHECK_EQUAL(success.metrics_relabeled_due_to_collision, 0); + BOOST_CHECK_EQUAL(count_by_label("lev"), 1); + BOOST_CHECK_EQUAL(count_by_label("err"), 0); + sm::set_relabel_configs({}).get(); +} + +SEASTAR_THREAD_TEST_CASE(test_relabel_enable_disable_skip_when_empty) { + using namespace seastar::metrics; + namespace sm = seastar::metrics; + sm::metric_groups app_metrics; + app_metrics.add_group("test3", { + sm::make_gauge("gauge_1", sm::description("gague 1"), { sm::label_instance("lev3", "3")}, [] { return 0; }), + sm::make_counter("counter_1", sm::description("counter 1"), { sm::label_instance("lev3", "3")}, [] { return 0; }), + sm::make_counter("counter_2", sm::description("counter 2"), { sm::label_instance("lev3", "3")}, [] { return 0; }) + }); + std::vector<sm::relabel_config> rl(2); + rl[0].source_labels = {"__name__"}; + rl[0].action = sm::relabel_config::relabel_action::drop; + + rl[1].source_labels = {"lev3"}; + rl[1].expr = "3"; + rl[1].action = sm::relabel_config::relabel_action::keep; + // We just disable all metrics besides those mark as lev3 + sm::metric_relabeling_result success = sm::set_relabel_configs(rl).get(); + BOOST_CHECK_EQUAL(success.metrics_relabeled_due_to_collision, 0); + BOOST_CHECK_EQUAL(count_by_label(""), 3); + BOOST_CHECK_EQUAL(count_by_fun([](const seastar::metrics::impl::metric_info& mi) { + return mi.should_skip_when_empty == sm::skip_when_empty::yes; + }), 0); + + std::vector<sm::relabel_config> rl2(3); + rl2[0].source_labels = {"__name__"}; + rl2[0].action = sm::relabel_config::relabel_action::drop; + + rl2[1].source_labels = {"lev3"}; + rl2[1].expr = "3"; + rl2[1].action = sm::relabel_config::relabel_action::keep; + + rl2[2].source_labels = {"__name__"}; + rl2[2].expr = "test3.*"; + rl2[2].action = sm::relabel_config::relabel_action::skip_when_empty; + + success = sm::set_relabel_configs(rl2).get(); + BOOST_CHECK_EQUAL(success.metrics_relabeled_due_to_collision, 0); + BOOST_CHECK_EQUAL(count_by_label(""), 3); + BOOST_CHECK_EQUAL(count_by_fun([](const seastar::metrics::impl::metric_info& mi) { + return mi.should_skip_when_empty == sm::skip_when_empty::yes; + }), 3); + // clear the configuration + success = sm::set_relabel_configs({}).get(); + app_metrics.add_group("test3", { + sm::make_counter("counter_3", sm::description("counter 2"), { sm::label_instance("lev3", "3")}, [] { return 0; })(sm::skip_when_empty::yes) + }); + std::vector<sm::relabel_config> rl3(3); + rl3[0].source_labels = {"__name__"}; + rl3[0].action = sm::relabel_config::relabel_action::drop; + + rl3[1].source_labels = {"lev3"}; + rl3[1].expr = "3"; + rl3[1].action = sm::relabel_config::relabel_action::keep; + + rl3[2].source_labels = {"__name__"}; + rl3[2].expr = "test3.*"; + rl3[2].action = sm::relabel_config::relabel_action::report_when_empty; + + success = sm::set_relabel_configs(rl3).get(); + BOOST_CHECK_EQUAL(success.metrics_relabeled_due_to_collision, 0); + BOOST_CHECK_EQUAL(count_by_fun([](const seastar::metrics::impl::metric_info& mi) { + return mi.should_skip_when_empty == sm::skip_when_empty::yes; + }), 0); + sm::set_relabel_configs({}).get(); +} |