Skip to content

Commit 1704e22

Browse files
fix: safely initialize exemplar reservoir storage (#8524)
1 parent 2ce864f commit 1704e22

1 file changed

Lines changed: 22 additions & 10 deletions

File tree

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/exemplar/FixedSizeExemplarReservoir.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@
2020
/** Base for fixed-size reservoir sampling of Exemplars. */
2121
class FixedSizeExemplarReservoir implements DoubleExemplarReservoir, LongExemplarReservoir {
2222

23-
@Nullable private ReservoirCell[] storage;
23+
@Nullable private volatile ReservoirCell[] storage;
2424
private final ReservoirCellSelector reservoirCellSelector;
2525
private final int size;
2626
private final Clock clock;
27+
private final Object storageLock = new Object();
2728
private volatile boolean hasMeasurements = false;
2829

2930
/** Instantiates an exemplar reservoir of fixed size. */
@@ -36,12 +37,10 @@ class FixedSizeExemplarReservoir implements DoubleExemplarReservoir, LongExempla
3637

3738
@Override
3839
public void offerLongMeasurement(long value, Attributes attributes, Context context) {
39-
if (storage == null) {
40-
storage = initStorage();
41-
}
40+
ReservoirCell[] storage = getOrInitStorage();
4241
int bucket = reservoirCellSelector.reservoirCellIndexFor(storage, value, attributes, context);
4342
if (bucket != -1) {
44-
this.storage[bucket].recordLongMeasurement(value, attributes, context);
43+
storage[bucket].recordLongMeasurement(value, attributes, context);
4544
this.hasMeasurements = true;
4645
}
4746
}
@@ -58,16 +57,28 @@ public List<LongExemplarData> collectAndResetLongs(Attributes pointAttributes) {
5857

5958
@Override
6059
public void offerDoubleMeasurement(double value, Attributes attributes, Context context) {
61-
if (storage == null) {
62-
storage = initStorage();
63-
}
60+
ReservoirCell[] storage = getOrInitStorage();
6461
int bucket = reservoirCellSelector.reservoirCellIndexFor(storage, value, attributes, context);
6562
if (bucket != -1) {
66-
this.storage[bucket].recordDoubleMeasurement(value, attributes, context);
63+
storage[bucket].recordDoubleMeasurement(value, attributes, context);
6764
this.hasMeasurements = true;
6865
}
6966
}
7067

68+
private ReservoirCell[] getOrInitStorage() {
69+
ReservoirCell[] storage = this.storage;
70+
if (storage == null) {
71+
synchronized (storageLock) {
72+
storage = this.storage;
73+
if (storage == null) {
74+
storage = initStorage();
75+
this.storage = storage;
76+
}
77+
}
78+
}
79+
return storage;
80+
}
81+
7182
private ReservoirCell[] initStorage() {
7283
ReservoirCell[] storage = new ReservoirCell[this.size];
7384
for (int i = 0; i < size; ++i) {
@@ -78,13 +89,14 @@ private ReservoirCell[] initStorage() {
7889

7990
public <T extends ExemplarData> List<T> doCollectAndReset(
8091
Attributes pointAttributes, BiFunction<ReservoirCell, Attributes, T> mapAndResetCell) {
92+
ReservoirCell[] storage = this.storage;
8193
if (!hasMeasurements || storage == null) {
8294
return Collections.emptyList();
8395
}
8496
// Note: we are collecting exemplars from buckets piecemeal, but we
8597
// could still be sampling exemplars during this process.
8698
List<T> results = new ArrayList<>();
87-
for (ReservoirCell reservoirCell : this.storage) {
99+
for (ReservoirCell reservoirCell : storage) {
88100
T result = mapAndResetCell.apply(reservoirCell, pointAttributes);
89101
if (result != null) {
90102
results.add(result);

0 commit comments

Comments
 (0)