]> git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/jaeger-client-cpp/src/jaegertracing/samplers/SamplerTest.cpp
buildsys: switch source download to quincy
[ceph.git] / ceph / src / jaegertracing / jaeger-client-cpp / src / jaegertracing / samplers / SamplerTest.cpp
1 /*
2 * Copyright (c) 2017 Uber Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <random>
18
19 #include <gtest/gtest.h>
20
21 #include "jaegertracing/Constants.h"
22 #include "jaegertracing/Tag.h"
23 #include "jaegertracing/samplers/AdaptiveSampler.h"
24 #include "jaegertracing/samplers/Config.h"
25 #include "jaegertracing/samplers/ConstSampler.h"
26 #include "jaegertracing/samplers/GuaranteedThroughputProbabilisticSampler.h"
27 #include "jaegertracing/samplers/ProbabilisticSampler.h"
28 #include "jaegertracing/samplers/RateLimitingSampler.h"
29 #include "jaegertracing/samplers/RemotelyControlledSampler.h"
30 #include "jaegertracing/samplers/Sampler.h"
31 #include "jaegertracing/samplers/SamplingStatus.h"
32 #include "jaegertracing/testutils/MockAgent.h"
33 #include "jaegertracing/testutils/TUDPTransport.h"
34 #include "jaegertracing/thrift-gen/jaeger_types.h"
35
36 namespace jaegertracing {
37 namespace samplers {
38 namespace {
39
40 constexpr auto kTestOperationName = "op";
41 constexpr auto kTestFirstTimeOperationName = "firstTimeOp";
42 constexpr auto kTestDefaultSamplingProbability = 0.5;
43 constexpr auto kTestMaxID = std::numeric_limits<uint64_t>::max() / 2 + 1;
44 constexpr auto kTestDefaultMaxOperations = 10;
45
46 const Tag testProbablisticExpectedTags[] = {
47 { "sampler.type", "probabilistic" }, { "sampler.param", 0.5 }
48 };
49
50 const Tag testLowerBoundExpectedTags[] = { { "sampler.type", "lowerbound" },
51 { "sampler.param", 0.5 } };
52
53 #define CMP_TAGS(tagArr, tagVec) \
54 { \
55 ASSERT_EQ(sizeof(tagArr) / sizeof(Tag), (tagVec).size()); \
56 for (auto i = static_cast<size_t>(0); i < (tagVec).size(); ++i) { \
57 jaegertracing::thrift::Tag thriftTagArr; \
58 jaegertracing::thrift::Tag thriftTagVec; \
59 (tagArr)[i].thrift(thriftTagArr); \
60 (tagVec)[i].thrift(thriftTagVec); \
61 ASSERT_EQ(thriftTagArr, thriftTagVec); \
62 } \
63 }
64
65 } // anonymous namespace
66
67 TEST(Sampler, testSamplerTags)
68 {
69 ConstSampler constTrue(true);
70 ConstSampler constFalse(false);
71 ProbabilisticSampler prob(0.1);
72 RateLimitingSampler rate(0.1);
73
74 const struct {
75 Sampler& _sampler;
76 std::string _samplerType;
77 Tag::ValueType _samplerParam;
78 } tests[] = { { constTrue, "const", true },
79 { constFalse, "const", false },
80 { prob, "probabilistic", 0.1 },
81 { rate, "ratelimiting", 0.1 } };
82
83 for (auto&& test : tests) {
84 const auto tags =
85 test._sampler.isSampled(TraceID(), kTestOperationName).tags();
86 auto count = 0;
87 for (auto&& tag : tags) {
88 if (tag.key() == kSamplerTypeTagKey) {
89 ASSERT_TRUE(tag.value().is<const char*>());
90 ASSERT_EQ(test._samplerType, tag.value().get<const char*>());
91 ++count;
92 }
93 else if (tag.key() == kSamplerParamTagKey) {
94 ASSERT_EQ(test._samplerParam, tag.value());
95 ++count;
96 }
97 }
98 ASSERT_EQ(2, count);
99 }
100 }
101
102 TEST(Sampler, testProbabilisticSamplerErrors)
103 {
104 ProbabilisticSampler sampler(-0.1);
105 ASSERT_LE(0, sampler.samplingRate());
106 ASSERT_GE(1, sampler.samplingRate());
107 sampler = ProbabilisticSampler(1.1);
108 ASSERT_LE(0, sampler.samplingRate());
109 ASSERT_GE(1, sampler.samplingRate());
110 }
111
112 TEST(Sampler, testProbabilisticSampler)
113 {
114 {
115 ProbabilisticSampler sampler(0.5);
116 auto result =
117 sampler.isSampled(TraceID(0, kTestMaxID + 10), kTestOperationName);
118 ASSERT_FALSE(result.isSampled());
119 CMP_TAGS(testProbablisticExpectedTags, result.tags());
120
121 result =
122 sampler.isSampled(TraceID(0, kTestMaxID - 20), kTestOperationName);
123 ASSERT_TRUE(result.isSampled());
124 CMP_TAGS(testProbablisticExpectedTags, result.tags());
125 }
126 {
127 ProbabilisticSampler sampler(1.0);
128 auto result =
129 sampler.isSampled(TraceID(0, kTestMaxID), kTestOperationName);
130 ASSERT_TRUE(result.isSampled());
131
132 result =
133 sampler.isSampled(TraceID(0, kTestMaxID - 20), kTestOperationName);
134 ASSERT_TRUE(result.isSampled());
135 }
136 }
137
138 TEST(Sampler, testProbabilisticSamplerPerformance)
139 {
140 constexpr auto kNumSamples = static_cast<uint64_t>(10000);
141
142 ProbabilisticSampler sampler(0.001);
143 std::random_device randomDevice;
144 std::default_random_engine randomGenerator(randomDevice());
145 std::uniform_int_distribution<uint64_t> distribution;
146 auto count = static_cast<uint64_t>(0);
147 for (auto i = static_cast<uint64_t>(0); i < kNumSamples; ++i) {
148 TraceID id(0, distribution(randomGenerator));
149 if (sampler.isSampled(id, kTestOperationName).isSampled()) {
150 ++count;
151 }
152 }
153 const auto rate = static_cast<double>(count) / kNumSamples;
154 std::cout << "Sampled: " << count << " rate=" << rate << '\n';
155 }
156
157 TEST(Sampler, testProbabilisticSamplerInvalidRate)
158 {
159 Config samplerConfig1(kSamplerTypeProbabilistic,
160 1.1,
161 "",
162 0,
163 samplers::Config::Clock::duration());
164 Config samplerConfig2(kSamplerTypeProbabilistic,
165 -0.1,
166 "",
167 0,
168 samplers::Config::Clock::duration());
169 auto logger = logging::nullLogger();
170 auto metrics = metrics::Metrics::makeNullMetrics();
171 ASSERT_THROW(samplerConfig1.makeSampler("test-service", *logger, *metrics),
172 std::invalid_argument);
173 ASSERT_THROW(samplerConfig2.makeSampler("test-service", *logger, *metrics),
174 std::invalid_argument);
175 }
176
177 TEST(Sampler, testRateLimitingSampler)
178 {
179 {
180 RateLimitingSampler sampler(2);
181 auto result = sampler.isSampled(TraceID(), kTestOperationName);
182 ASSERT_TRUE(result.isSampled());
183 result = sampler.isSampled(TraceID(), kTestOperationName);
184 ASSERT_TRUE(result.isSampled());
185 result = sampler.isSampled(TraceID(), kTestOperationName);
186 ASSERT_FALSE(result.isSampled());
187 }
188
189 {
190 RateLimitingSampler sampler(0.1);
191 auto result = sampler.isSampled(TraceID(), kTestOperationName);
192 ASSERT_TRUE(result.isSampled());
193 result = sampler.isSampled(TraceID(), kTestOperationName);
194 ASSERT_FALSE(result.isSampled());
195 }
196 }
197
198 TEST(Sampler, testGuaranteedThroughputProbabilisticSamplerUpdate)
199 {
200 auto lowerBound = 2.0;
201 auto samplingRate = 0.5;
202 GuaranteedThroughputProbabilisticSampler sampler(lowerBound, samplingRate);
203 ASSERT_EQ(lowerBound, sampler.lowerBound());
204 ASSERT_EQ(samplingRate, sampler.samplingRate());
205
206 auto newLowerBound = 1.0;
207 auto newSamplingRate = 0.6;
208 sampler.update(newLowerBound, newSamplingRate);
209 ASSERT_EQ(newLowerBound, sampler.lowerBound());
210 ASSERT_EQ(newSamplingRate, sampler.samplingRate());
211
212 newSamplingRate = 1.1;
213 sampler.update(newLowerBound, newSamplingRate);
214 ASSERT_EQ(1.0, sampler.samplingRate());
215 }
216
217 TEST(Sampler, testAdaptiveSampler)
218 {
219 namespace thriftgen = sampling_manager::thrift;
220
221 thriftgen::OperationSamplingStrategy strategy;
222 strategy.__set_operation(kTestOperationName);
223 thriftgen::ProbabilisticSamplingStrategy probabilisticSampling;
224 probabilisticSampling.__set_samplingRate(kTestDefaultSamplingProbability);
225 strategy.__set_probabilisticSampling(probabilisticSampling);
226
227 thriftgen::PerOperationSamplingStrategies strategies;
228 strategies.__set_defaultSamplingProbability(
229 kTestDefaultSamplingProbability);
230 strategies.__set_defaultLowerBoundTracesPerSecond(1.0);
231 strategies.__set_perOperationStrategies({ strategy });
232
233 AdaptiveSampler sampler(strategies, kTestDefaultMaxOperations);
234 auto result =
235 sampler.isSampled(TraceID(0, kTestMaxID + 10), kTestOperationName);
236 ASSERT_TRUE(result.isSampled());
237 CMP_TAGS(testLowerBoundExpectedTags, result.tags());
238
239 result = sampler.isSampled(TraceID(0, kTestMaxID - 20), kTestOperationName);
240 ASSERT_TRUE(result.isSampled());
241 CMP_TAGS(testProbablisticExpectedTags, result.tags());
242
243 result = sampler.isSampled(TraceID(0, kTestMaxID + 10), kTestOperationName);
244 ASSERT_FALSE(result.isSampled());
245
246 result = sampler.isSampled(TraceID(0, kTestMaxID - 20),
247 kTestFirstTimeOperationName);
248 ASSERT_TRUE(result.isSampled());
249 CMP_TAGS(testProbablisticExpectedTags, result.tags());
250 }
251
252 TEST(Sampler, testAdaptiveSamplerErrors)
253 {
254 namespace thriftgen = sampling_manager::thrift;
255
256 thriftgen::OperationSamplingStrategy strategy;
257 strategy.__set_operation(kTestOperationName);
258 thriftgen::ProbabilisticSamplingStrategy probabilisticSampling;
259 probabilisticSampling.__set_samplingRate(-0.1);
260 strategy.__set_probabilisticSampling(probabilisticSampling);
261
262 thriftgen::PerOperationSamplingStrategies strategies;
263 strategies.__set_defaultSamplingProbability(
264 kTestDefaultSamplingProbability);
265 strategies.__set_defaultLowerBoundTracesPerSecond(2.0);
266 strategies.__set_perOperationStrategies({ strategy });
267
268 {
269 AdaptiveSampler sampler(strategies, kTestDefaultMaxOperations);
270 }
271
272 {
273 strategies.perOperationStrategies.at(0)
274 .probabilisticSampling.__set_samplingRate(1.1);
275 AdaptiveSampler sampler(strategies, kTestDefaultMaxOperations);
276 }
277 }
278
279 TEST(Sampler, testAdaptiveSamplerUpdate)
280 {
281 namespace thriftgen = sampling_manager::thrift;
282
283 constexpr auto kSamplingRate = 0.1;
284 constexpr auto kLowerBound = 2.0;
285
286 thriftgen::OperationSamplingStrategy strategy;
287 strategy.__set_operation(kTestOperationName);
288 thriftgen::ProbabilisticSamplingStrategy probabilisticSampling;
289 probabilisticSampling.__set_samplingRate(kSamplingRate);
290 strategy.__set_probabilisticSampling(probabilisticSampling);
291
292 thriftgen::PerOperationSamplingStrategies strategies;
293 strategies.__set_defaultSamplingProbability(
294 kTestDefaultSamplingProbability);
295 strategies.__set_defaultLowerBoundTracesPerSecond(kLowerBound);
296 strategies.__set_perOperationStrategies({ strategy });
297
298 AdaptiveSampler sampler(strategies, kTestDefaultMaxOperations);
299
300 constexpr auto kNewSamplingRate = 0.2;
301 constexpr auto kNewLowerBound = 3.0;
302 constexpr auto kNewDefaultSamplingProbability = 0.1;
303
304 // Updated kTestOperationName strategy.
305 thriftgen::OperationSamplingStrategy updatedStrategy;
306 updatedStrategy.__set_operation(kTestOperationName);
307 thriftgen::ProbabilisticSamplingStrategy updatedProbabilisticSampling;
308 updatedProbabilisticSampling.__set_samplingRate(kNewSamplingRate);
309 updatedStrategy.__set_probabilisticSampling(updatedProbabilisticSampling);
310
311 // New kTestFirstTimeOperationName strategy.
312 thriftgen::OperationSamplingStrategy newStrategy;
313 newStrategy.__set_operation(kTestFirstTimeOperationName);
314 thriftgen::ProbabilisticSamplingStrategy newProbabilisticSampling;
315 newProbabilisticSampling.__set_samplingRate(kNewSamplingRate);
316 newStrategy.__set_probabilisticSampling(newProbabilisticSampling);
317
318 thriftgen::PerOperationSamplingStrategies newStrategies;
319 newStrategies.__set_defaultSamplingProbability(
320 kNewDefaultSamplingProbability);
321 newStrategies.__set_defaultLowerBoundTracesPerSecond(kNewLowerBound);
322 newStrategies.__set_perOperationStrategies(
323 { updatedStrategy, newStrategy });
324
325 sampler.update(newStrategies);
326 }
327
328 TEST(Sampler, testRemotelyControlledSampler)
329 {
330 const auto mockAgent = testutils::MockAgent::make();
331 mockAgent->start();
332 const auto logger = logging::nullLogger();
333 const auto metrics = metrics::Metrics::makeNullMetrics();
334
335 // Make sure remote sampling probability is 1
336 sampling_manager::thrift::SamplingStrategyResponse config;
337 config.__set_strategyType(
338 sampling_manager::thrift::SamplingStrategyType::PROBABILISTIC);
339 sampling_manager::thrift::ProbabilisticSamplingStrategy probaStrategy;
340 probaStrategy.__set_samplingRate(1.0);
341 config.__set_probabilisticSampling(probaStrategy);
342 mockAgent->addSamplingStrategy("test-service", config);
343
344 // Default probability of 0.5, switches to 1 when downloaded
345 RemotelyControlledSampler sampler(
346 "test-service",
347 "http://" + mockAgent->samplingServerAddress().authority(),
348 std::make_shared<ProbabilisticSampler>(kTestDefaultSamplingProbability),
349 kTestDefaultMaxOperations,
350 std::chrono::milliseconds(100),
351 *logger,
352 *metrics);
353
354 // Wait a bit for remote config download to be done
355 std::this_thread::sleep_for(std::chrono::milliseconds(100));
356 std::random_device device;
357 std::mt19937_64 rng;
358 rng.seed(device());
359 for (auto startTime = RemotelyControlledSampler::Clock::now();
360 std::chrono::duration_cast<std::chrono::seconds>(
361 RemotelyControlledSampler::Clock::now() - startTime)
362 .count() < 1;) {
363 TraceID traceID(rng(), rng());
364 // If probability was 0.5 we could reasonnably assume one of 50 samples fail
365 ASSERT_TRUE(sampler.isSampled(traceID, kTestOperationName).isSampled());
366 std::this_thread::sleep_for(std::chrono::milliseconds(20));
367 }
368 sampler.close();
369 }
370
371 } // namespace samplers
372 } // namespace jaegertracing