]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/tests/unit/metrics_test.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / tests / unit / metrics_test.cc
1 /*
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.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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
16 * under the License.
17 */
18
19 /*
20 * Copyright (C) 2019 ScyllaDB.
21 */
22
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>
38
39 SEASTAR_TEST_CASE(test_add_group) {
40 using namespace seastar::metrics;
41 // Just has to compile:
42 metric_groups()
43 .add_group("g1", {})
44 .add_group("g2", std::vector<metric_definition>());
45 return seastar::make_ready_future();
46 }
47
48 /**
49 * This function return the different name label values
50 * for the named metric.
51 *
52 * @note: If the statistic or label doesn't exist, the test
53 * that calls this function will fail.
54 *
55 * @param metric_name - the metric name
56 * @param label_name - the label name
57 * @return a set containing all the different values
58 * of the label.
59 */
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);
72 }
73 return labels;
74 }
75
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;
82
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());
100 }
101
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);
111 });
112 });
113 }).get();
114
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));
120 }
121
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
133 // stats.
134 return pc.update_shares(101);
135 }).get();
136
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());
151 }
152
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);
162 });
163 });
164 }).get();
165
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));
171 }
172
173 int count_by_label(const std::string& label) {
174 seastar::foreign_ptr<seastar::metrics::impl::values_reference> values = seastar::metrics::impl::get_values();
175 int count = 0;
176 for (auto&& md : (*values->metadata)) {
177 for (auto&& mi : md.metrics) {
178 if (label == "" || mi.id.labels().find(label) != mi.id.labels().end()) {
179 count++;
180 }
181 }
182 }
183 return count;
184 }
185
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();
188 int count = 0;
189 for (auto&& md : (*values->metadata)) {
190 for (auto&& mi : md.metrics) {
191 if (f(mi)) {
192 count++;
193 }
194 }
195 }
196 return count;
197 }
198
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; })
206 });
207
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_.*";
213
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; })
219 });
220 BOOST_CHECK_EQUAL(count_by_label("level"), 2);
221 sm::set_relabel_configs({}).get();
222 }
223
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; })
232 });
233 BOOST_CHECK_EQUAL(count_by_label("lev"), 1);
234
235 std::vector<sm::relabel_config> rl(1);
236 rl[0].source_labels = {"lev"};
237 rl[0].expr = "2";
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);
245
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();
252 }
253
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; })
262 });
263 std::vector<sm::relabel_config> rl(2);
264 rl[0].source_labels = {"__name__"};
265 rl[0].action = sm::relabel_config::relabel_action::drop;
266
267 rl[1].source_labels = {"lev3"};
268 rl[1].expr = "3";
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;
276 }), 0);
277
278 std::vector<sm::relabel_config> rl2(3);
279 rl2[0].source_labels = {"__name__"};
280 rl2[0].action = sm::relabel_config::relabel_action::drop;
281
282 rl2[1].source_labels = {"lev3"};
283 rl2[1].expr = "3";
284 rl2[1].action = sm::relabel_config::relabel_action::keep;
285
286 rl2[2].source_labels = {"__name__"};
287 rl2[2].expr = "test3.*";
288 rl2[2].action = sm::relabel_config::relabel_action::skip_when_empty;
289
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;
295 }), 3);
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)
300 });
301 std::vector<sm::relabel_config> rl3(3);
302 rl3[0].source_labels = {"__name__"};
303 rl3[0].action = sm::relabel_config::relabel_action::drop;
304
305 rl3[1].source_labels = {"lev3"};
306 rl3[1].expr = "3";
307 rl3[1].action = sm::relabel_config::relabel_action::keep;
308
309 rl3[2].source_labels = {"__name__"};
310 rl3[2].expr = "test3.*";
311 rl3[2].action = sm::relabel_config::relabel_action::report_when_empty;
312
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;
317 }), 0);
318 sm::set_relabel_configs({}).get();
319 }