Skip to content

Commit 3d6f699

Browse files
committed
CKMS Quantiles: Fix test and disallow corner cases
Signed-off-by: Fabian Stäber <[email protected]>
1 parent 787eef3 commit 3d6f699

File tree

3 files changed

+22
-51
lines changed

3 files changed

+22
-51
lines changed

simpleclient/src/main/java/io/prometheus/client/CKMSQuantiles.java

+4-8
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,10 @@ final class CKMSQuantiles {
129129
* @param quantiles The targeted quantiles, can be empty.
130130
*/
131131
CKMSQuantiles(Quantile[] quantiles) {
132-
// hard-coded epsilon of 0.1% to determine the batch size, and default epsilon in case of empty quantiles
133-
double pointOnePercent = 0.001;
134132
if (quantiles.length == 0) { // we need at least one for this algorithm to work
135-
this.quantiles = new Quantile[1];
136-
this.quantiles[0] = new Quantile(0.5, pointOnePercent / 2);
137-
} else {
138-
this.quantiles = quantiles;
133+
throw new IllegalArgumentException("quantiles cannot be empty");
139134
}
135+
this.quantiles = quantiles;
140136

141137
// section 5.1 Methods - Batch.
142138
// This is hardcoded to 500, which corresponds to an epsilon of 0.1%.
@@ -431,8 +427,8 @@ static class Quantile {
431427
* @param epsilon the desired error for this quantile, between 0 and 1.
432428
*/
433429
Quantile(double quantile, double epsilon) {
434-
if (quantile < 0 || quantile > 1.0) throw new IllegalArgumentException("Quantile must be between 0 and 1");
435-
if (epsilon < 0 || epsilon > 1.0) throw new IllegalArgumentException("Epsilon must be between 0 and 1");
430+
if (quantile <= 0 || quantile >= 1.0) throw new IllegalArgumentException("Quantile must be between 0 and 1");
431+
if (epsilon <= 0 || epsilon >= 1.0) throw new IllegalArgumentException("Epsilon must be between 0 and 1");
436432

437433
this.quantile = quantile;
438434
this.epsilon = epsilon;

simpleclient/src/main/java/io/prometheus/client/Summary.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,10 @@ public static class Builder extends SimpleCollector.Builder<Builder, Summary> {
9999
private int ageBuckets = 5;
100100

101101
public Builder quantile(double quantile, double error) {
102-
if (quantile < 0.0 || quantile > 1.0) {
102+
if (quantile <= 0.0 || quantile >= 1.0) {
103103
throw new IllegalArgumentException("Quantile " + quantile + " invalid: Expected number between 0.0 and 1.0.");
104104
}
105-
if (error < 0.0 || error > 1.0) {
105+
if (error <= 0.0 || error >= 1.0) {
106106
throw new IllegalArgumentException("Error " + error + " invalid: Expected number between 0.0 and 1.0.");
107107
}
108108
quantiles.add(new Quantile(quantile, error));

simpleclient/src/test/java/io/prometheus/client/CKMSQuantilesTest.java

+16-41
Original file line numberDiff line numberDiff line change
@@ -25,37 +25,6 @@ public void testGetOnEmptyValues() {
2525
assertEquals(Double.NaN, ckms.get(0), 0);
2626
}
2727

28-
@Test
29-
public void testGetWhenNoQuantilesAreDefined() {
30-
CKMSQuantiles ckms = new CKMSQuantiles(new Quantile[]{});
31-
assertEquals(Double.NaN, ckms.get(0), 0);
32-
}
33-
34-
@Test
35-
public void testInsertWhenNoQuantilesAreDefined() {
36-
CKMSQuantiles ckms = new CKMSQuantiles(new Quantile[]{});
37-
ckms.insert(1.0);
38-
ckms.insert(2.0);
39-
ckms.insert(3.0);
40-
assertEquals(1.0, ckms.get(0), 0);
41-
assertEquals(2.0, ckms.get(0.5), 0);
42-
assertEquals(3.0, ckms.get(1), 0);
43-
}
44-
45-
@Test
46-
public void testCompressWhenBufferSize500Reached() {
47-
CKMSQuantiles ckms = new CKMSQuantiles(new Quantile[]{});
48-
List<Double> input = makeSequence(1, 499);
49-
50-
for (double v : input) {
51-
ckms.insert(v);
52-
}
53-
assertEquals("No compress should be triggered", 0, ckms.samples.size());
54-
55-
ckms.insert(500);
56-
assertEquals(500, ckms.samples.size());
57-
}
58-
5928
@Test
6029
public void testGet() {
6130
List<Quantile> quantiles = new ArrayList<Quantile>();
@@ -80,20 +49,19 @@ public void testGet() {
8049
@Test
8150
public void testGetWithAMillionElements() {
8251
List<Quantile> quantiles = new ArrayList<Quantile>();
83-
quantiles.add(new Quantile(0.0, 0.01));
52+
quantiles.add(new Quantile(0.01, 0.001));
8453
quantiles.add(new Quantile(0.10, 0.01));
8554
quantiles.add(new Quantile(0.90, 0.001));
8655
quantiles.add(new Quantile(0.95, 0.02));
8756
quantiles.add(new Quantile(0.99, 0.001));
8857

8958
final int elemCount = 1000000;
90-
double[] shuffle = new double[elemCount];
91-
for (int i = 0; i < shuffle.length; i++) {
92-
shuffle[i] = i + 1;
59+
List<Double> shuffle = new ArrayList<Double>(elemCount);
60+
for (int i = 0; i < elemCount; i++) {
61+
shuffle.add(i+1.0);
9362
}
9463
Random rand = new Random(0);
95-
96-
Collections.shuffle(Arrays.asList(shuffle), rand);
64+
Collections.shuffle(shuffle, rand);
9765

9866
CKMSQuantiles ckms = new CKMSQuantiles(
9967
quantiles.toArray(new Quantile[]{}));
@@ -102,14 +70,21 @@ public void testGetWithAMillionElements() {
10270
ckms.insert(v);
10371
}
10472
// given the linear distribution, we set the delta equal to the εn value for this quantile
105-
assertEquals(0.1 * elemCount, ckms.get(0.1), 0.01 * elemCount);
106-
assertEquals(0.9 * elemCount, ckms.get(0.9), 0.001 * elemCount);
107-
assertEquals(0.95 * elemCount, ckms.get(0.95), 0.02 * elemCount);
108-
assertEquals(0.99 * elemCount, ckms.get(0.99), 0.001 * elemCount);
73+
assertRank(elemCount, ckms.get(0.01), 0.01, 0.001);
74+
assertRank(elemCount, ckms.get(0.1), 0.1, 0.01);
75+
assertRank(elemCount, ckms.get(0.9), 0.9, 0.001);
76+
assertRank(elemCount, ckms.get(0.95), 0.95, 0.02);
77+
assertRank(elemCount, ckms.get(0.99), 0.99, 0.001);
10978

11079
assertTrue("sample size should be way below 1_000_000", ckms.samples.size() < 1000);
11180
}
11281

82+
private void assertRank(int elemCount, double actual, double quantile, double epsilon) {
83+
double lowerBound = elemCount * (quantile - epsilon);
84+
double upperBound = elemCount * (quantile + epsilon);
85+
assertTrue("quantile=" + quantile + ", actual=" + actual + ", lowerBound=" + lowerBound, actual >= lowerBound);
86+
assertTrue("quantile=" + quantile + ", actual=" + actual + ", upperBound=" + upperBound, actual <= upperBound);
87+
}
11388

11489
@Test
11590
public void testGetGaussian() {

0 commit comments

Comments
 (0)