Skip to content

Commit 5e7ed74

Browse files
fix: correctly apply fractional sample rates (#408)
1 parent 52da15a commit 5e7ed74

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

‎openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/selector/DefaultTrafficSelector.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public boolean shouldRequestBeValidated(RequestMetaData request) {
4545

4646
private boolean isRandomlySelectedBySampleRate() {
4747
return sampleRate > 0
48-
&& (sampleRate >= 1 || ThreadLocalRandom.current().nextInt((int) (1 / sampleRate)) == 0);
48+
&& (sampleRate >= 1 || ThreadLocalRandom.current().nextDouble() < sampleRate);
4949
}
5050

5151
@Override

‎openapi-validation-api/src/test/java/com/getyourguide/openapi/validation/api/selector/DefaultTrafficSelectorTest.java‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.getyourguide.openapi.validation.api.selector;
22

33
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
45

56
import com.getyourguide.openapi.validation.api.exclusions.ExcludedHeader;
67
import com.getyourguide.openapi.validation.api.model.RequestMetaData;
@@ -9,9 +10,14 @@
910
import java.util.Map;
1011
import java.util.TreeMap;
1112
import java.util.regex.Pattern;
13+
import java.util.stream.IntStream;
14+
1215
import org.junit.jupiter.api.Test;
1316

1417
class DefaultTrafficSelectorTest {
18+
private static final int SAMPLE_SIZE = 10000;
19+
private static final double ACCEPTABLE_SAMPLE_RATE_DEVIATION = 0.02;
20+
1521
private final TrafficSelector trafficSelector = new DefaultTrafficSelector(
1622
1.0,
1723
null,
@@ -69,4 +75,32 @@ private Map<String, String> toCaseInsensitiveMap(Map<String, String> map) {
6975
newMap.putAll(map);
7076
return newMap;
7177
}
78+
79+
@Test
80+
public void testSampleRateIsApproximatelyCorrectForReciprocalValues() {
81+
List.of(0.1, 0.25, 0.5).forEach(this::assertSampleRateIsApproximatelyCorrect);
82+
}
83+
84+
@Test
85+
public void testSampleRateIsApproximatelyCorrectForNonReciprocalValues() {
86+
List.of(0.3, 0.8).forEach(this::assertSampleRateIsApproximatelyCorrect);
87+
}
88+
89+
private void assertSampleRateIsApproximatelyCorrect(double sampleRate) {
90+
var sampleRateTrafficSelector = new DefaultTrafficSelector(sampleRate, null, null);
91+
var request = new RequestMetaData(
92+
"GET",
93+
URI.create("https://api.example.com/v1/path"),
94+
Map.of("Content-Type", "application/json")
95+
);
96+
var validatedRequestCount = IntStream.range(0, SAMPLE_SIZE)
97+
.filter(ignored -> sampleRateTrafficSelector.shouldRequestBeValidated(request))
98+
.count();
99+
var actualSampleRate = (double) validatedRequestCount / SAMPLE_SIZE;
100+
101+
assertTrue(
102+
Math.abs(actualSampleRate - sampleRate) < ACCEPTABLE_SAMPLE_RATE_DEVIATION,
103+
"Expected a sample rate close to " + sampleRate + ", but was " + actualSampleRate
104+
);
105+
}
72106
}

0 commit comments

Comments
 (0)