2020/** Base for fixed-size reservoir sampling of Exemplars. */
2121class 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