Skip to content

Commit 48b3d8e

Browse files
committed
Add test
1 parent 0b2afed commit 48b3d8e

File tree

2 files changed

+207
-6
lines changed

2 files changed

+207
-6
lines changed

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/sampling/RateLimitedSamplingPercentage.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@
3434

3535
// uses adaptive algorithm from OpenTelemetry Java Contrib's ConsistentRateLimitingSampler
3636
// (https://github.com/open-telemetry/opentelemetry-java-contrib/blob/main/consistent-sampling/src/main/java/io/opentelemetry/contrib/samplers/ConsistentRateLimitingSampler.java)
37-
//
38-
// TODO see if we can port test over also
3937
class RateLimitedSamplingPercentage implements SamplingPercentage {
4038

4139
private static final class State {
@@ -54,15 +52,18 @@ private State(double effectiveWindowCount, double effectiveWindowNanos, long las
5452
private final double inverseAdaptationTimeNanos;
5553
private final double targetSpansPerNanosecondLimit;
5654
private final AtomicReference<State> state;
55+
private final boolean roundToNearest;
5756

5857
RateLimitedSamplingPercentage(double targetSpansPerSecondLimit, double adaptationTimeSeconds) {
59-
this(targetSpansPerSecondLimit, adaptationTimeSeconds, System::nanoTime);
58+
this(targetSpansPerSecondLimit, adaptationTimeSeconds, System::nanoTime, true);
6059
}
6160

62-
private RateLimitedSamplingPercentage(
61+
// visible for testing
62+
RateLimitedSamplingPercentage(
6363
double targetSpansPerSecondLimit,
6464
double adaptationTimeSeconds,
65-
LongSupplier nanoTimeSupplier) {
65+
LongSupplier nanoTimeSupplier,
66+
boolean roundToNearest) {
6667

6768
if (targetSpansPerSecondLimit < 0.0) {
6869
throw new IllegalArgumentException("Limit for sampled spans per second must be nonnegative!");
@@ -76,6 +77,8 @@ private RateLimitedSamplingPercentage(
7677
this.targetSpansPerNanosecondLimit = 1e-9 * targetSpansPerSecondLimit;
7778

7879
this.state = new AtomicReference<>(new State(0, 0, nanoTimeSupplier.getAsLong()));
80+
81+
this.roundToNearest = roundToNearest;
7982
}
8083

8184
private State updateState(State oldState, long currentNanoTime) {
@@ -102,7 +105,10 @@ public double get() {
102105

103106
double samplingPercentage = 100 * Math.min(samplingProbability, 1);
104107

105-
return roundDownToNearest(samplingPercentage);
108+
if (roundToNearest) {
109+
samplingPercentage = roundDownToNearest(samplingPercentage);
110+
}
111+
return samplingPercentage;
106112
}
107113

108114
private static double roundDownToNearest(double samplingPercentage) {
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/*
2+
* ApplicationInsights-Java
3+
* Copyright (c) Microsoft Corporation
4+
* All rights reserved.
5+
*
6+
* MIT License
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
8+
* software and associated documentation files (the ""Software""), to deal in the Software
9+
* without restriction, including without limitation the rights to use, copy, modify, merge,
10+
* publish, distribute, sublicense, and/or sell copies of the Software, and to permit
11+
* persons to whom the Software is furnished to do so, subject to the following conditions:
12+
* The above copyright notice and this permission notice shall be included in all copies or
13+
* substantial portions of the Software.
14+
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15+
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
16+
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
17+
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19+
* DEALINGS IN THE SOFTWARE.
20+
*/
21+
22+
// Includes work from:
23+
/*
24+
* Copyright The OpenTelemetry Authors
25+
* SPDX-License-Identifier: Apache-2.0
26+
*/
27+
28+
package com.microsoft.applicationinsights.agent.internal.sampling;
29+
30+
import org.assertj.core.data.Percentage;
31+
import org.junit.jupiter.api.BeforeEach;
32+
import org.junit.jupiter.api.Test;
33+
34+
import java.util.ArrayList;
35+
import java.util.List;
36+
import java.util.concurrent.ThreadLocalRandom;
37+
import java.util.concurrent.TimeUnit;
38+
import java.util.function.LongSupplier;
39+
40+
import static org.assertj.core.api.Assertions.assertThat;
41+
42+
// uses tests from OpenTelemetry Java Contrib's ConsistentRateLimitingSampler
43+
// (https://github.com/open-telemetry/opentelemetry-java-contrib/blob/main/consistent-sampling/src/test/java/io/opentelemetry/contrib/samplers/ConsistentRateLimitingSamplerTest.java)
44+
class RateLimitedSamplingPercentageTest {
45+
46+
private long[] nanoTime;
47+
private LongSupplier nanoTimeSupplier;
48+
49+
@BeforeEach
50+
void init() {
51+
nanoTime = new long[] {0L};
52+
nanoTimeSupplier = () -> nanoTime[0];
53+
}
54+
55+
private void advanceTime(long nanosIncrement) {
56+
nanoTime[0] += nanosIncrement;
57+
}
58+
59+
private long getCurrentTimeNanos() {
60+
return nanoTime[0];
61+
}
62+
63+
@Test
64+
void testConstantRate() {
65+
66+
double targetSpansPerSecondLimit = 1000;
67+
double adaptationTimeSeconds = 5;
68+
69+
RateLimitedSamplingPercentage samplingPercentage =
70+
new RateLimitedSamplingPercentage(
71+
targetSpansPerSecondLimit, adaptationTimeSeconds, nanoTimeSupplier, false);
72+
73+
long nanosBetweenSpans = TimeUnit.MICROSECONDS.toNanos(100);
74+
int numSpans = 1000000;
75+
76+
List<Long> spanSampledNanos = new ArrayList<>();
77+
78+
for (int i = 0; i < numSpans; ++i) {
79+
advanceTime(nanosBetweenSpans);
80+
if (ThreadLocalRandom.current().nextDouble() < samplingPercentage.get() / 100) {
81+
spanSampledNanos.add(getCurrentTimeNanos());
82+
}
83+
}
84+
85+
long numSampledSpansInLast5Seconds =
86+
spanSampledNanos.stream()
87+
.filter(x -> x > TimeUnit.SECONDS.toNanos(95) && x <= TimeUnit.SECONDS.toNanos(100))
88+
.count();
89+
90+
assertThat(numSampledSpansInLast5Seconds / 5.)
91+
.isCloseTo(targetSpansPerSecondLimit, Percentage.withPercentage(5));
92+
}
93+
94+
@Test
95+
void testRateIncrease() {
96+
97+
double targetSpansPerSecondLimit = 1000;
98+
double adaptationTimeSeconds = 5;
99+
100+
RateLimitedSamplingPercentage samplingPercentage =
101+
new RateLimitedSamplingPercentage(
102+
targetSpansPerSecondLimit, adaptationTimeSeconds, nanoTimeSupplier, false);
103+
104+
long nanosBetweenSpans1 = TimeUnit.MICROSECONDS.toNanos(100);
105+
long nanosBetweenSpans2 = TimeUnit.MICROSECONDS.toNanos(10);
106+
int numSpans1 = 500000;
107+
int numSpans2 = 5000000;
108+
109+
List<Long> spanSampledNanos = new ArrayList<>();
110+
111+
for (int i = 0; i < numSpans1; ++i) {
112+
advanceTime(nanosBetweenSpans1);
113+
if (ThreadLocalRandom.current().nextDouble() < samplingPercentage.get() / 100) {
114+
spanSampledNanos.add(getCurrentTimeNanos());
115+
}
116+
}
117+
for (int i = 0; i < numSpans2; ++i) {
118+
advanceTime(nanosBetweenSpans2);
119+
if (ThreadLocalRandom.current().nextDouble() < samplingPercentage.get() / 100) {
120+
spanSampledNanos.add(getCurrentTimeNanos());
121+
}
122+
}
123+
124+
long numSampledSpansWithin5SecondsBeforeChange =
125+
spanSampledNanos.stream()
126+
.filter(x -> x > TimeUnit.SECONDS.toNanos(45) && x <= TimeUnit.SECONDS.toNanos(50))
127+
.count();
128+
long numSampledSpansWithin5SecondsAfterChange =
129+
spanSampledNanos.stream()
130+
.filter(x -> x > TimeUnit.SECONDS.toNanos(50) && x <= TimeUnit.SECONDS.toNanos(55))
131+
.count();
132+
long numSampledSpansInLast5Seconds =
133+
spanSampledNanos.stream()
134+
.filter(x -> x > TimeUnit.SECONDS.toNanos(95) && x <= TimeUnit.SECONDS.toNanos(100))
135+
.count();
136+
137+
assertThat(numSampledSpansWithin5SecondsBeforeChange / 5.)
138+
.isCloseTo(targetSpansPerSecondLimit, Percentage.withPercentage(5));
139+
assertThat(numSampledSpansWithin5SecondsAfterChange / 5.)
140+
.isGreaterThan(2. * targetSpansPerSecondLimit);
141+
assertThat(numSampledSpansInLast5Seconds / 5.)
142+
.isCloseTo(targetSpansPerSecondLimit, Percentage.withPercentage(5));
143+
}
144+
145+
@Test
146+
void testRateDecrease() {
147+
148+
double targetSpansPerSecondLimit = 1000;
149+
double adaptationTimeSeconds = 5;
150+
151+
RateLimitedSamplingPercentage samplingPercentage =
152+
new RateLimitedSamplingPercentage(
153+
targetSpansPerSecondLimit, adaptationTimeSeconds, nanoTimeSupplier, false);
154+
155+
long nanosBetweenSpans1 = TimeUnit.MICROSECONDS.toNanos(10);
156+
long nanosBetweenSpans2 = TimeUnit.MICROSECONDS.toNanos(100);
157+
int numSpans1 = 5000000;
158+
int numSpans2 = 500000;
159+
160+
List<Long> spanSampledNanos = new ArrayList<>();
161+
162+
for (int i = 0; i < numSpans1; ++i) {
163+
advanceTime(nanosBetweenSpans1);
164+
if (ThreadLocalRandom.current().nextDouble() < samplingPercentage.get() / 100) {
165+
spanSampledNanos.add(getCurrentTimeNanos());
166+
}
167+
}
168+
for (int i = 0; i < numSpans2; ++i) {
169+
advanceTime(nanosBetweenSpans2);
170+
if (ThreadLocalRandom.current().nextDouble() < samplingPercentage.get() / 100) {
171+
spanSampledNanos.add(getCurrentTimeNanos());
172+
}
173+
}
174+
175+
long numSampledSpansWithin5SecondsBeforeChange =
176+
spanSampledNanos.stream()
177+
.filter(x -> x > TimeUnit.SECONDS.toNanos(45) && x <= TimeUnit.SECONDS.toNanos(50))
178+
.count();
179+
long numSampledSpansWithin5SecondsAfterChange =
180+
spanSampledNanos.stream()
181+
.filter(x -> x > TimeUnit.SECONDS.toNanos(50) && x <= TimeUnit.SECONDS.toNanos(55))
182+
.count();
183+
long numSampledSpansInLast5Seconds =
184+
spanSampledNanos.stream()
185+
.filter(x -> x > TimeUnit.SECONDS.toNanos(95) && x <= TimeUnit.SECONDS.toNanos(100))
186+
.count();
187+
188+
assertThat(numSampledSpansWithin5SecondsBeforeChange / 5.)
189+
.isCloseTo(targetSpansPerSecondLimit, Percentage.withPercentage(5));
190+
assertThat(numSampledSpansWithin5SecondsAfterChange / 5.)
191+
.isLessThan(0.5 * targetSpansPerSecondLimit);
192+
assertThat(numSampledSpansInLast5Seconds / 5.)
193+
.isCloseTo(targetSpansPerSecondLimit, Percentage.withPercentage(5));
194+
}
195+
}

0 commit comments

Comments
 (0)