2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
20 * Copyright (C) 2019 ScyllaDB.
23 #include <seastar/core/metrics_registration.hh>
24 #include <seastar/core/metrics.hh>
25 #include <seastar/core/metrics_api.hh>
26 #include <seastar/core/relabel_config.hh>
27 #include <seastar/core/reactor.hh>
28 #include <seastar/core/scheduling.hh>
29 #include <seastar/core/sleep.hh>
30 #include <seastar/core/sharded.hh>
31 #include <seastar/core/do_with.hh>
32 #include <seastar/core/io_queue.hh>
33 #include <seastar/core/loop.hh>
34 #include <seastar/testing/test_case.hh>
35 #include <seastar/testing/thread_test_case.hh>
36 #include <seastar/testing/test_runner.hh>
37 #include <boost/range/irange.hpp>
39 SEASTAR_TEST_CASE(test_add_group
) {
40 using namespace seastar::metrics
;
41 // Just has to compile:
44 .add_group("g2", std::vector
<metric_definition
>());
45 return seastar::make_ready_future();
49 * This function return the different name label values
50 * for the named metric.
52 * @note: If the statistic or label doesn't exist, the test
53 * that calls this function will fail.
55 * @param metric_name - the metric name
56 * @param label_name - the label name
57 * @return a set containing all the different values
60 static std::set
<seastar::sstring
> get_label_values(seastar::sstring metric_name
, seastar::sstring label_name
) {
61 namespace smi
= seastar::metrics::impl
;
62 auto all_metrics
= smi::get_values();
63 const auto& all_metadata
= *all_metrics
->metadata
;
64 const auto qp_group
= find_if(cbegin(all_metadata
), cend(all_metadata
),
65 [&metric_name
] (const auto& x
) { return x
.mf
.name
== metric_name
; });
66 BOOST_REQUIRE(qp_group
!= cend(all_metadata
));
67 std::set
<seastar::sstring
> labels
;
68 for (const auto& metric
: qp_group
->metrics
) {
69 const auto found
= metric
.id
.labels().find(label_name
);
70 BOOST_REQUIRE(found
!= metric
.id
.labels().cend());
71 labels
.insert(found
->second
);
76 SEASTAR_THREAD_TEST_CASE(test_renaming_scheuling_groups
) {
77 // this seams a little bit out of place but the
78 // renaming functionality is primarily for statistics
79 // otherwise those classes could have just been reused
80 // without renaming them.
81 using namespace seastar
;
83 static const char* name1
= "A";
84 static const char* name2
= "B";
85 scheduling_group sg
= create_scheduling_group("hello", 111).get0();
86 boost::integer_range
<int> rng(0, 1000);
87 // repeatedly change the group name back and forth in
88 // decresing time intervals to see if it generate double
89 //registration statistics errors.
90 for (auto&& i
: rng
) {
91 const char* name
= i
%2 ? name1
: name2
;
92 const char* prev_name
= i
%2 ? name2
: name1
;
93 sleep(std::chrono::microseconds(100000/(i
+1))).get();
94 rename_scheduling_group(sg
, name
).get();
95 std::set
<sstring
> label_vals
= get_label_values(sstring("scheduler_shares"), sstring("group"));
96 // validate that the name that we *renamed to* is in the stats
97 BOOST_REQUIRE(label_vals
.find(sstring(name
)) != label_vals
.end());
98 // validate that the name that we *renamed from* is *not* in the stats
99 BOOST_REQUIRE(label_vals
.find(sstring(prev_name
)) == label_vals
.end());
102 smp::invoke_on_all([sg
] () {
103 return do_with(std::uniform_int_distribution
<int>(), boost::irange
<int>(0, 1000),
104 [sg
] (std::uniform_int_distribution
<int>& dist
, boost::integer_range
<int>& rng
) {
105 // flip a fair coin and rename to one of two options and rename to that
106 // scheduling group name, do it 1000 in parallel on all shards so there
107 // is a chance of collision.
108 return do_for_each(rng
, [sg
, &dist
] (auto i
) {
109 bool odd
= dist(seastar::testing::local_random_engine
)%2;
110 return rename_scheduling_group(sg
, odd
? name1
: name2
);
115 std::set
<sstring
> label_vals
= get_label_values(sstring("scheduler_shares"), sstring("group"));
116 // validate that only one of the names is eventually in the metrics
117 bool name1_found
= label_vals
.find(sstring(name1
)) != label_vals
.end();
118 bool name2_found
= label_vals
.find(sstring(name2
)) != label_vals
.end();
119 BOOST_REQUIRE((name1_found
&& !name2_found
) || (name2_found
&& !name1_found
));
122 SEASTAR_THREAD_TEST_CASE(test_renaming_io_priority_classes
) {
123 // this seams a little bit out of place but the
124 // renaming functionality is primarily for statistics
125 // otherwise those classes could have just been reused
126 // without renaming them.
127 using namespace seastar
;
128 static const char* name1
= "A";
129 static const char* name2
= "B";
130 seastar::io_priority_class pc
= io_priority_class::register_one("hello",100);
131 smp::invoke_on_all([&pc
] () {
132 // this is a trick to get all of the queues actually register their
134 return pc
.update_shares(101);
137 boost::integer_range
<int> rng(0, 1000);
138 // repeatedly change the group name back and forth in
139 // decresing time intervals to see if it generate double
140 //registration statistics errors.
141 for (auto&& i
: rng
) {
142 const char* name
= i
%2 ? name1
: name2
;
143 const char* prev_name
= i
%2 ? name2
: name1
;
144 sleep(std::chrono::microseconds(100000/(i
+1))).get();
145 pc
.rename(name
).get();
146 std::set
<sstring
> label_vals
= get_label_values(sstring("io_queue_shares"), sstring("class"));
147 // validate that the name that we *renamed to* is in the stats
148 BOOST_REQUIRE(label_vals
.find(sstring(name
)) != label_vals
.end());
149 // validate that the name that we *renamed from* is *not* in the stats
150 BOOST_REQUIRE(label_vals
.find(sstring(prev_name
)) == label_vals
.end());
153 smp::invoke_on_all([&pc
] () {
154 return do_with(std::uniform_int_distribution
<int>(), boost::irange
<int>(0, 1000),
155 [&pc
] (std::uniform_int_distribution
<int>& dist
, boost::integer_range
<int>& rng
) {
156 // flip a fair coin and rename to one of two options and rename to that
157 // scheduling group name, do it 1000 in parallel on all shards so there
158 // is a chance of collision.
159 return do_for_each(rng
, [&pc
, &dist
] (auto i
) {
160 bool odd
= dist(seastar::testing::local_random_engine
)%2;
161 return pc
.rename(odd
? name1
: name2
);
166 std::set
<sstring
> label_vals
= get_label_values(sstring("io_queue_shares"), sstring("class"));
167 // validate that only one of the names is eventually in the metrics
168 bool name1_found
= label_vals
.find(sstring(name1
)) != label_vals
.end();
169 bool name2_found
= label_vals
.find(sstring(name2
)) != label_vals
.end();
170 BOOST_REQUIRE((name1_found
&& !name2_found
) || (name2_found
&& !name1_found
));
173 int count_by_label(const std::string
& label
) {
174 seastar::foreign_ptr
<seastar::metrics::impl::values_reference
> values
= seastar::metrics::impl::get_values();
176 for (auto&& md
: (*values
->metadata
)) {
177 for (auto&& mi
: md
.metrics
) {
178 if (label
== "" || mi
.id
.labels().find(label
) != mi
.id
.labels().end()) {
186 int count_by_fun(std::function
<bool(const seastar::metrics::impl::metric_info
&)> f
) {
187 seastar::foreign_ptr
<seastar::metrics::impl::values_reference
> values
= seastar::metrics::impl::get_values();
189 for (auto&& md
: (*values
->metadata
)) {
190 for (auto&& mi
: md
.metrics
) {
199 SEASTAR_THREAD_TEST_CASE(test_relabel_add_labels
) {
200 using namespace seastar::metrics
;
201 namespace sm
= seastar::metrics
;
202 sm::metric_groups app_metrics
;
203 app_metrics
.add_group("test", {
204 sm::make_gauge("gauge_1", sm::description("gague 1"), [] { return 0; }),
205 sm::make_counter("counter_1", sm::description("counter 1"), [] { return 1; })
208 std::vector
<sm::relabel_config
> rl(1);
209 rl
[0].source_labels
= {"__name__"};
210 rl
[0].target_label
= "level";
211 rl
[0].replacement
= "1";
212 rl
[0].expr
= "test_counter_.*";
214 sm::metric_relabeling_result success
= sm::set_relabel_configs(rl
).get();
215 BOOST_CHECK_EQUAL(success
.metrics_relabeled_due_to_collision
, 0);
216 BOOST_CHECK_EQUAL(count_by_label("level"), 1);
217 app_metrics
.add_group("test", {
218 sm::make_counter("counter_2", sm::description("counter 2"), [] { return 2; })
220 BOOST_CHECK_EQUAL(count_by_label("level"), 2);
221 sm::set_relabel_configs({}).get();
224 SEASTAR_THREAD_TEST_CASE(test_relabel_drop_label_prevent_runtime_conflicts
) {
225 using namespace seastar::metrics
;
226 namespace sm
= seastar::metrics
;
227 sm::metric_groups app_metrics
;
228 app_metrics
.add_group("test2", {
229 sm::make_gauge("gauge_1", sm::description("gague 1"), { sm::label_instance("g", "1")}, [] { return 0; }),
230 sm::make_counter("counter_1", sm::description("counter 1"), [] { return 0; }),
231 sm::make_counter("counter_1", sm::description("counter 1"), { sm::label_instance("lev", "2")}, [] { return 0; })
233 BOOST_CHECK_EQUAL(count_by_label("lev"), 1);
235 std::vector
<sm::relabel_config
> rl(1);
236 rl
[0].source_labels
= {"lev"};
238 rl
[0].target_label
= "lev";
239 rl
[0].action
= sm::relabel_config::relabel_action::drop_label
;
240 // Dropping the lev label would cause a conflict, but not crash the system
241 sm::metric_relabeling_result success
= sm::set_relabel_configs(rl
).get();
242 BOOST_CHECK_EQUAL(success
.metrics_relabeled_due_to_collision
, 1);
243 BOOST_CHECK_EQUAL(count_by_label("lev"), 0);
244 BOOST_CHECK_EQUAL(count_by_label("err"), 1);
246 //reseting all the labels to their original state
247 success
= sm::set_relabel_configs({}).get();
248 BOOST_CHECK_EQUAL(success
.metrics_relabeled_due_to_collision
, 0);
249 BOOST_CHECK_EQUAL(count_by_label("lev"), 1);
250 BOOST_CHECK_EQUAL(count_by_label("err"), 0);
251 sm::set_relabel_configs({}).get();
254 SEASTAR_THREAD_TEST_CASE(test_relabel_enable_disable_skip_when_empty
) {
255 using namespace seastar::metrics
;
256 namespace sm
= seastar::metrics
;
257 sm::metric_groups app_metrics
;
258 app_metrics
.add_group("test3", {
259 sm::make_gauge("gauge_1", sm::description("gague 1"), { sm::label_instance("lev3", "3")}, [] { return 0; }),
260 sm::make_counter("counter_1", sm::description("counter 1"), { sm::label_instance("lev3", "3")}, [] { return 0; }),
261 sm::make_counter("counter_2", sm::description("counter 2"), { sm::label_instance("lev3", "3")}, [] { return 0; })
263 std::vector
<sm::relabel_config
> rl(2);
264 rl
[0].source_labels
= {"__name__"};
265 rl
[0].action
= sm::relabel_config::relabel_action::drop
;
267 rl
[1].source_labels
= {"lev3"};
269 rl
[1].action
= sm::relabel_config::relabel_action::keep
;
270 // We just disable all metrics besides those mark as lev3
271 sm::metric_relabeling_result success
= sm::set_relabel_configs(rl
).get();
272 BOOST_CHECK_EQUAL(success
.metrics_relabeled_due_to_collision
, 0);
273 BOOST_CHECK_EQUAL(count_by_label(""), 3);
274 BOOST_CHECK_EQUAL(count_by_fun([](const seastar::metrics::impl::metric_info
& mi
) {
275 return mi
.should_skip_when_empty
== sm::skip_when_empty::yes
;
278 std::vector
<sm::relabel_config
> rl2(3);
279 rl2
[0].source_labels
= {"__name__"};
280 rl2
[0].action
= sm::relabel_config::relabel_action::drop
;
282 rl2
[1].source_labels
= {"lev3"};
284 rl2
[1].action
= sm::relabel_config::relabel_action::keep
;
286 rl2
[2].source_labels
= {"__name__"};
287 rl2
[2].expr
= "test3.*";
288 rl2
[2].action
= sm::relabel_config::relabel_action::skip_when_empty
;
290 success
= sm::set_relabel_configs(rl2
).get();
291 BOOST_CHECK_EQUAL(success
.metrics_relabeled_due_to_collision
, 0);
292 BOOST_CHECK_EQUAL(count_by_label(""), 3);
293 BOOST_CHECK_EQUAL(count_by_fun([](const seastar::metrics::impl::metric_info
& mi
) {
294 return mi
.should_skip_when_empty
== sm::skip_when_empty::yes
;
296 // clear the configuration
297 success
= sm::set_relabel_configs({}).get();
298 app_metrics
.add_group("test3", {
299 sm::make_counter("counter_3", sm::description("counter 2"), { sm::label_instance("lev3", "3")}, [] { return 0; })(sm::skip_when_empty::yes
)
301 std::vector
<sm::relabel_config
> rl3(3);
302 rl3
[0].source_labels
= {"__name__"};
303 rl3
[0].action
= sm::relabel_config::relabel_action::drop
;
305 rl3
[1].source_labels
= {"lev3"};
307 rl3
[1].action
= sm::relabel_config::relabel_action::keep
;
309 rl3
[2].source_labels
= {"__name__"};
310 rl3
[2].expr
= "test3.*";
311 rl3
[2].action
= sm::relabel_config::relabel_action::report_when_empty
;
313 success
= sm::set_relabel_configs(rl3
).get();
314 BOOST_CHECK_EQUAL(success
.metrics_relabeled_due_to_collision
, 0);
315 BOOST_CHECK_EQUAL(count_by_fun([](const seastar::metrics::impl::metric_info
& mi
) {
316 return mi
.should_skip_when_empty
== sm::skip_when_empty::yes
;
318 sm::set_relabel_configs({}).get();