Skip to content

Commit 80dfc76

Browse files
committed
Merge branch '1.13.x'
2 parents 2df569e + 1ef1111 commit 80dfc76

File tree

4 files changed

+150
-28
lines changed

4 files changed

+150
-28
lines changed

micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/FixedBoundaryHistogram.java

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,32 @@ class FixedBoundaryHistogram {
2727

2828
private final boolean isCumulativeBucketCounts;
2929

30+
/**
31+
* Creates a FixedBoundaryHistogram which tracks the count of values for each bucket
32+
* bound).
33+
* @param buckets - sorted bucket boundaries
34+
* @param isCumulativeBucketCounts - whether the count values should be cumulative
35+
* count of lower buckets and current bucket.
36+
*/
3037
FixedBoundaryHistogram(double[] buckets, boolean isCumulativeBucketCounts) {
3138
this.buckets = buckets;
3239
this.values = new AtomicLongArray(buckets.length);
3340
this.isCumulativeBucketCounts = isCumulativeBucketCounts;
3441
}
3542

36-
long countAtValue(double value) {
37-
int index = Arrays.binarySearch(buckets, value);
43+
double[] getBuckets() {
44+
return this.buckets;
45+
}
46+
47+
/**
48+
* Returns the number of values that was recorded between previous bucket and the
49+
* queried bucket (upper bound inclusive)
50+
* @param bucket - the bucket to find values for
51+
* @return 0 if bucket is not a valid bucket otherwise number of values recorded
52+
* between (index(bucket) - 1, bucket]
53+
*/
54+
private long countAtBucket(double bucket) {
55+
int index = Arrays.binarySearch(buckets, bucket);
3856
if (index < 0)
3957
return 0;
4058
return values.get(index);
@@ -53,18 +71,20 @@ void record(long value) {
5371
}
5472

5573
/**
56-
* The least bucket that is less than or equal to a sample.
74+
* The least bucket that is less than or equal to a valueToRecord. Returns -1, if the
75+
* valueToRecord is greater than the highest bucket.
5776
*/
58-
int leastLessThanOrEqualTo(double key) {
77+
// VisibleForTesting
78+
int leastLessThanOrEqualTo(long valueToRecord) {
5979
int low = 0;
6080
int high = buckets.length - 1;
6181

6282
while (low <= high) {
6383
int mid = (low + high) >>> 1;
64-
double value = buckets[mid];
65-
if (value < key)
84+
double bucket = buckets[mid];
85+
if (bucket < valueToRecord)
6686
low = mid + 1;
67-
else if (value > key)
87+
else if (bucket > valueToRecord)
6888
high = mid - 1;
6989
else
7090
return mid; // exact match
@@ -73,28 +93,49 @@ else if (value > key)
7393
return low < buckets.length ? low : -1;
7494
}
7595

76-
Iterator<CountAtBucket> countsAtValues(Iterator<Double> values) {
96+
Iterator<CountAtBucket> countsAtValues(Iterator<Double> buckets) {
7797
return new Iterator<CountAtBucket>() {
7898
private double cumulativeCount = 0.0;
7999

80100
@Override
81101
public boolean hasNext() {
82-
return values.hasNext();
102+
return buckets.hasNext();
83103
}
84104

85105
@Override
86106
public CountAtBucket next() {
87-
double value = values.next();
88-
double count = countAtValue(value);
107+
double bucket = buckets.next();
108+
double count = countAtBucket(bucket);
89109
if (isCumulativeBucketCounts) {
90110
cumulativeCount += count;
91-
return new CountAtBucket(value, cumulativeCount);
111+
return new CountAtBucket(bucket, cumulativeCount);
92112
}
93113
else {
94-
return new CountAtBucket(value, count);
114+
return new CountAtBucket(bucket, count);
95115
}
96116
}
97117
};
98118
}
99119

120+
/**
121+
* Returns the list of {@link CountAtBucket} for each of the buckets tracked by this
122+
* histogram.
123+
*/
124+
CountAtBucket[] getCountsAtBucket() {
125+
CountAtBucket[] countAtBuckets = new CountAtBucket[this.buckets.length];
126+
long cumulativeCount = 0;
127+
128+
for (int i = 0; i < this.buckets.length; i++) {
129+
final long valueAtCurrentBucket = values.get(i);
130+
if (isCumulativeBucketCounts) {
131+
cumulativeCount += valueAtCurrentBucket;
132+
countAtBuckets[i] = new CountAtBucket(buckets[i], cumulativeCount);
133+
}
134+
else {
135+
countAtBuckets[i] = new CountAtBucket(buckets[i], valueAtCurrentBucket);
136+
}
137+
}
138+
return countAtBuckets;
139+
}
140+
100141
}

micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/StepBucketHistogram.java

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
import io.micrometer.core.instrument.config.InvalidConfigurationException;
2020
import io.micrometer.core.instrument.step.StepValue;
2121

22-
import java.util.Arrays;
23-
import java.util.Iterator;
2422
import java.util.NavigableSet;
2523
import java.util.Objects;
2624
import java.util.function.Supplier;
@@ -36,16 +34,14 @@ public class StepBucketHistogram extends StepValue<CountAtBucket[]> implements H
3634

3735
private final FixedBoundaryHistogram fixedBoundaryHistogram;
3836

39-
private final double[] buckets;
40-
4137
public StepBucketHistogram(Clock clock, long stepMillis, DistributionStatisticConfig distributionStatisticConfig,
4238
boolean supportsAggregablePercentiles, boolean isCumulativeBucketCounts) {
4339
super(clock, stepMillis, getEmptyCounts(
4440
getBucketsFromDistributionStatisticConfig(distributionStatisticConfig, supportsAggregablePercentiles)));
4541

46-
this.buckets = getBucketsFromDistributionStatisticConfig(distributionStatisticConfig,
47-
supportsAggregablePercentiles);
48-
this.fixedBoundaryHistogram = new FixedBoundaryHistogram(buckets, isCumulativeBucketCounts);
42+
this.fixedBoundaryHistogram = new FixedBoundaryHistogram(
43+
getBucketsFromDistributionStatisticConfig(distributionStatisticConfig, supportsAggregablePercentiles),
44+
isCumulativeBucketCounts);
4945
}
5046

5147
@Override
@@ -66,13 +62,9 @@ public HistogramSnapshot takeSnapshot(long count, double total, double max) {
6662
@Override
6763
protected Supplier<CountAtBucket[]> valueSupplier() {
6864
return () -> {
69-
CountAtBucket[] countAtBuckets = new CountAtBucket[buckets.length];
65+
CountAtBucket[] countAtBuckets;
7066
synchronized (fixedBoundaryHistogram) {
71-
final Iterator<CountAtBucket> iterator = fixedBoundaryHistogram
72-
.countsAtValues(Arrays.stream(buckets).iterator());
73-
for (int i = 0; i < countAtBuckets.length; i++) {
74-
countAtBuckets[i] = iterator.next();
75-
}
67+
countAtBuckets = fixedBoundaryHistogram.getCountsAtBucket();
7668
fixedBoundaryHistogram.reset();
7769
}
7870
return countAtBuckets;
@@ -81,7 +73,7 @@ protected Supplier<CountAtBucket[]> valueSupplier() {
8173

8274
@Override
8375
protected CountAtBucket[] noValue() {
84-
return getEmptyCounts(buckets);
76+
return getEmptyCounts(this.fixedBoundaryHistogram.getBuckets());
8577
}
8678

8779
private static CountAtBucket[] getEmptyCounts(double[] buckets) {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2024 VMware, 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+
* https://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+
package io.micrometer.core.instrument.distribution;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import java.util.stream.Stream;
21+
22+
import org.junit.jupiter.api.BeforeEach;
23+
import org.junit.jupiter.api.Test;
24+
import org.junit.jupiter.params.ParameterizedTest;
25+
import org.junit.jupiter.params.provider.Arguments;
26+
import org.junit.jupiter.params.provider.MethodSource;
27+
28+
class FixedBoundaryHistogramTest {
29+
30+
private static final double[] BUCKET_BOUNDS = new double[] { 1, 10, 100 };
31+
32+
private FixedBoundaryHistogram fixedBoundaryHistogram;
33+
34+
@BeforeEach
35+
void setup() {
36+
fixedBoundaryHistogram = new FixedBoundaryHistogram(BUCKET_BOUNDS, false);
37+
}
38+
39+
@Test
40+
void testGetBuckets() {
41+
assertThat(fixedBoundaryHistogram.getBuckets()).containsExactly(BUCKET_BOUNDS);
42+
}
43+
44+
@ParameterizedTest
45+
@MethodSource("valuedIndexProvider")
46+
void testLeastLessThanOrEqualTo(long value, int expectedIndex) {
47+
assertThat(fixedBoundaryHistogram.leastLessThanOrEqualTo(value)).isEqualTo(expectedIndex);
48+
}
49+
50+
private static Stream<Arguments> valuedIndexProvider() {
51+
return Stream.of(Arguments.of(0, 0), Arguments.of(1, 0), Arguments.of(2, 1), Arguments.of(5, 1),
52+
Arguments.of(10, 1), Arguments.of(11, 2), Arguments.of(90, 2), Arguments.of(100, 2),
53+
Arguments.of(101, -1), Arguments.of(Long.MAX_VALUE, -1));
54+
}
55+
56+
@Test
57+
void testReset() {
58+
fixedBoundaryHistogram.record(1);
59+
fixedBoundaryHistogram.record(10);
60+
fixedBoundaryHistogram.record(100);
61+
assertThat(fixedBoundaryHistogram.getCountsAtBucket()).allMatch(countAtBucket -> countAtBucket.count() == 1);
62+
fixedBoundaryHistogram.reset();
63+
assertThat(fixedBoundaryHistogram.getCountsAtBucket()).allMatch(countAtBucket -> countAtBucket.count() == 0);
64+
}
65+
66+
@Test
67+
void testCountsAtBucket() {
68+
fixedBoundaryHistogram.record(1);
69+
fixedBoundaryHistogram.record(10);
70+
fixedBoundaryHistogram.record(100);
71+
assertThat(fixedBoundaryHistogram.getCountsAtBucket()).allMatch(countAtBucket -> countAtBucket.count() == 1);
72+
fixedBoundaryHistogram.reset();
73+
assertThat(fixedBoundaryHistogram.getCountsAtBucket()).allMatch(countAtBucket -> countAtBucket.count() == 0);
74+
fixedBoundaryHistogram.record(0);
75+
assertThat(fixedBoundaryHistogram.getCountsAtBucket()).containsExactly(new CountAtBucket(1.0, 1),
76+
new CountAtBucket(10.0, 0), new CountAtBucket(100.0, 0));
77+
}
78+
79+
@Test
80+
void testCumulativeCounts() {
81+
fixedBoundaryHistogram = new FixedBoundaryHistogram(BUCKET_BOUNDS, true);
82+
fixedBoundaryHistogram.record(1);
83+
fixedBoundaryHistogram.record(10);
84+
fixedBoundaryHistogram.record(100);
85+
assertThat(fixedBoundaryHistogram.getCountsAtBucket()).containsExactly(new CountAtBucket(1.0, 1),
86+
new CountAtBucket(10.0, 2), new CountAtBucket(100.0, 3));
87+
}
88+
89+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
import static org.assertj.core.api.Assertions.assertThat;
2525

26-
class StepHistogramTest {
26+
class StepBucketHistogramTest {
2727

2828
MockClock clock = new MockClock();
2929

0 commit comments

Comments
 (0)