Skip to content

Commit 6502e3b

Browse files
committed
Move / extend SynchronousInstrumentStressTest
1 parent db3110e commit 6502e3b

1 file changed

Lines changed: 171 additions & 23 deletions

File tree

sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SynchronousInstrumentStressTest.java renamed to sdk/metrics/src/testIncubating/java/io/opentelemetry/sdk/metrics/SynchronousInstrumentStressTest.java

Lines changed: 171 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,22 @@
1818
import com.google.common.util.concurrent.Uninterruptibles;
1919
import io.opentelemetry.api.common.AttributeKey;
2020
import io.opentelemetry.api.common.Attributes;
21+
import io.opentelemetry.api.incubator.metrics.BoundDoubleCounter;
22+
import io.opentelemetry.api.incubator.metrics.BoundDoubleGauge;
23+
import io.opentelemetry.api.incubator.metrics.BoundDoubleHistogram;
24+
import io.opentelemetry.api.incubator.metrics.BoundDoubleUpDownCounter;
25+
import io.opentelemetry.api.incubator.metrics.BoundLongCounter;
26+
import io.opentelemetry.api.incubator.metrics.BoundLongGauge;
27+
import io.opentelemetry.api.incubator.metrics.BoundLongHistogram;
28+
import io.opentelemetry.api.incubator.metrics.BoundLongUpDownCounter;
29+
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleCounter;
30+
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleGauge;
31+
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleHistogram;
32+
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleUpDownCounter;
33+
import io.opentelemetry.api.incubator.metrics.ExtendedLongCounter;
34+
import io.opentelemetry.api.incubator.metrics.ExtendedLongGauge;
35+
import io.opentelemetry.api.incubator.metrics.ExtendedLongHistogram;
36+
import io.opentelemetry.api.incubator.metrics.ExtendedLongUpDownCounter;
2137
import io.opentelemetry.api.metrics.Meter;
2238
import io.opentelemetry.internal.testing.CleanupExtension;
2339
import io.opentelemetry.sdk.common.export.MemoryMode;
@@ -44,7 +60,9 @@
4460
import java.util.ArrayList;
4561
import java.util.Arrays;
4662
import java.util.Collections;
63+
import java.util.HashMap;
4764
import java.util.List;
65+
import java.util.Map;
4866
import java.util.concurrent.CountDownLatch;
4967
import java.util.concurrent.atomic.AtomicLong;
5068
import java.util.stream.LongStream;
@@ -56,10 +74,14 @@
5674

5775
/**
5876
* {@link #stressTest(AggregationTemporality, InstrumentType, Aggregation, MemoryMode,
59-
* InstrumentValueType)} performs a stress test to confirm simultaneous record and collections do
60-
* not have concurrency issues like lost writes, partial writes, duplicate writes, etc. All
61-
* combinations of the following dimensions are tested: aggregation temporality, instrument type
62-
* (synchronous), memory mode, instrument value type.
77+
* InstrumentValueType, boolean)} performs a stress test to confirm simultaneous record and
78+
* collections do not have concurrency issues like lost writes, partial writes, duplicate writes,
79+
* etc. All combinations of the following dimensions are tested: aggregation temporality, instrument
80+
* type (synchronous), memory mode, instrument value type, and whether recording goes through bound
81+
* instruments ({@code Extended*#bind(Attributes)}) or unbound instruments.
82+
*
83+
* <p>Lives in {@code testIncubating} because the bound dimension exercises {@code
84+
* opentelemetry-api-incubator}, which the base {@code test} source set is not allowed to depend on.
6385
*/
6486
class SynchronousInstrumentStressTest {
6587

@@ -88,10 +110,16 @@ void stressTest(
88110
InstrumentType instrumentType,
89111
Aggregation aggregation,
90112
MemoryMode memoryMode,
91-
InstrumentValueType instrumentValueType) {
113+
InstrumentValueType instrumentValueType,
114+
boolean bound) {
92115
for (int repetition = 0; repetition < STRESS_TEST_REPETITIONS; repetition++) {
93116
stressTestOnce(
94-
aggregationTemporality, instrumentType, aggregation, memoryMode, instrumentValueType);
117+
aggregationTemporality,
118+
instrumentType,
119+
aggregation,
120+
memoryMode,
121+
instrumentValueType,
122+
bound);
95123
}
96124
}
97125

@@ -101,7 +129,8 @@ private void stressTestOnce(
101129
InstrumentType instrumentType,
102130
Aggregation aggregation,
103131
MemoryMode memoryMode,
104-
InstrumentValueType instrumentValueType) {
132+
InstrumentValueType instrumentValueType,
133+
boolean bound) {
105134
// Initialize metric SDK
106135
DefaultAggregationSelector aggregationSelector =
107136
DefaultAggregationSelector.getDefault().with(instrumentType, aggregation);
@@ -117,7 +146,8 @@ private void stressTestOnce(
117146
Meter meter = meterProvider.get("test");
118147
List<Attributes> attributes = Arrays.asList(ATTR_1, ATTR_2, ATTR_3, ATTR_4);
119148
Collections.shuffle(attributes);
120-
Instrument instrument = getInstrument(meter, instrumentType, instrumentValueType);
149+
Instrument instrument =
150+
getInstrument(meter, instrumentType, instrumentValueType, bound, attributes);
121151

122152
// Define list of measurements to record
123153
// Later, we'll assert that the data collected matches these measurements, with no lost writes,
@@ -298,20 +328,24 @@ private static Stream<Arguments> stressTestArgs() {
298328
InstrumentTypeAndAggregation.values()) {
299329
for (MemoryMode memoryMode : MemoryMode.values()) {
300330
for (InstrumentValueType instrumentValueType : InstrumentValueType.values()) {
301-
argumentsList.add(
302-
Arguments.argumentSet(
303-
aggregationTemporality
304-
+ " "
305-
+ instrumentTypeAndAggregation.instrumentType
306-
+ " "
307-
+ memoryMode
308-
+ " "
309-
+ instrumentValueType,
310-
aggregationTemporality,
311-
instrumentTypeAndAggregation.instrumentType,
312-
instrumentTypeAndAggregation.aggregation,
313-
memoryMode,
314-
instrumentValueType));
331+
for (boolean bound : new boolean[] {false, true}) {
332+
argumentsList.add(
333+
Arguments.argumentSet(
334+
aggregationTemporality
335+
+ " "
336+
+ instrumentTypeAndAggregation.instrumentType
337+
+ " "
338+
+ memoryMode
339+
+ " "
340+
+ instrumentValueType
341+
+ (bound ? " bound" : " unbound"),
342+
aggregationTemporality,
343+
instrumentTypeAndAggregation.instrumentType,
344+
instrumentTypeAndAggregation.aggregation,
345+
memoryMode,
346+
instrumentValueType,
347+
bound));
348+
}
315349
}
316350
}
317351
}
@@ -320,7 +354,19 @@ private static Stream<Arguments> stressTestArgs() {
320354
}
321355

322356
private static Instrument getInstrument(
323-
Meter meter, InstrumentType instrumentType, InstrumentValueType instrumentValueType) {
357+
Meter meter,
358+
InstrumentType instrumentType,
359+
InstrumentValueType instrumentValueType,
360+
boolean bound,
361+
List<Attributes> attributesList) {
362+
if (bound) {
363+
// Bind one bound instrument per attribute set up front, then dispatch records by attributes.
364+
// The bound record path is the one under test; the per-record map lookup here is just test
365+
// plumbing to reuse the unbound recording loop.
366+
Map<Attributes, BoundInstrument> boundInstruments =
367+
bindInstruments(meter, instrumentType, instrumentValueType, attributesList);
368+
return (value, attributes) -> boundInstruments.get(attributes).record(value);
369+
}
324370
switch (instrumentType) {
325371
case COUNTER:
326372
return instrumentValueType == InstrumentValueType.DOUBLE
@@ -354,10 +400,112 @@ private static Instrument getInstrument(
354400
throw new IllegalArgumentException();
355401
}
356402

403+
/**
404+
* Builds the instrument for the given type / value type, then binds one {@link BoundInstrument}
405+
* per attribute set in {@code attributesList}.
406+
*/
407+
private static Map<Attributes, BoundInstrument> bindInstruments(
408+
Meter meter,
409+
InstrumentType instrumentType,
410+
InstrumentValueType instrumentValueType,
411+
List<Attributes> attributesList) {
412+
Map<Attributes, BoundInstrument> result = new HashMap<>();
413+
boolean isDouble = instrumentValueType == InstrumentValueType.DOUBLE;
414+
switch (instrumentType) {
415+
case COUNTER:
416+
if (isDouble) {
417+
ExtendedDoubleCounter instrument =
418+
(ExtendedDoubleCounter) meter.counterBuilder(INSTRUMENT_NAME).ofDoubles().build();
419+
for (Attributes attributes : attributesList) {
420+
BoundDoubleCounter bound = instrument.bind(attributes);
421+
result.put(attributes, bound::add);
422+
}
423+
} else {
424+
ExtendedLongCounter instrument =
425+
(ExtendedLongCounter) meter.counterBuilder(INSTRUMENT_NAME).build();
426+
for (Attributes attributes : attributesList) {
427+
BoundLongCounter bound = instrument.bind(attributes);
428+
result.put(attributes, bound::add);
429+
}
430+
}
431+
return result;
432+
case UP_DOWN_COUNTER:
433+
if (isDouble) {
434+
ExtendedDoubleUpDownCounter instrument =
435+
(ExtendedDoubleUpDownCounter)
436+
meter.upDownCounterBuilder(INSTRUMENT_NAME).ofDoubles().build();
437+
for (Attributes attributes : attributesList) {
438+
BoundDoubleUpDownCounter bound = instrument.bind(attributes);
439+
result.put(attributes, bound::add);
440+
}
441+
} else {
442+
ExtendedLongUpDownCounter instrument =
443+
(ExtendedLongUpDownCounter) meter.upDownCounterBuilder(INSTRUMENT_NAME).build();
444+
for (Attributes attributes : attributesList) {
445+
BoundLongUpDownCounter bound = instrument.bind(attributes);
446+
result.put(attributes, bound::add);
447+
}
448+
}
449+
return result;
450+
case HISTOGRAM:
451+
if (isDouble) {
452+
ExtendedDoubleHistogram instrument =
453+
(ExtendedDoubleHistogram)
454+
meter
455+
.histogramBuilder(INSTRUMENT_NAME)
456+
.setExplicitBucketBoundariesAdvice(BUCKET_BOUNDARIES)
457+
.build();
458+
for (Attributes attributes : attributesList) {
459+
BoundDoubleHistogram bound = instrument.bind(attributes);
460+
result.put(attributes, bound::record);
461+
}
462+
} else {
463+
ExtendedLongHistogram instrument =
464+
(ExtendedLongHistogram)
465+
meter
466+
.histogramBuilder(INSTRUMENT_NAME)
467+
.setExplicitBucketBoundariesAdvice(BUCKET_BOUNDARIES)
468+
.ofLongs()
469+
.build();
470+
for (Attributes attributes : attributesList) {
471+
BoundLongHistogram bound = instrument.bind(attributes);
472+
result.put(attributes, bound::record);
473+
}
474+
}
475+
return result;
476+
case GAUGE:
477+
if (isDouble) {
478+
ExtendedDoubleGauge instrument =
479+
(ExtendedDoubleGauge) meter.gaugeBuilder(INSTRUMENT_NAME).build();
480+
for (Attributes attributes : attributesList) {
481+
BoundDoubleGauge bound = instrument.bind(attributes);
482+
result.put(attributes, bound::set);
483+
}
484+
} else {
485+
ExtendedLongGauge instrument =
486+
(ExtendedLongGauge) meter.gaugeBuilder(INSTRUMENT_NAME).ofLongs().build();
487+
for (Attributes attributes : attributesList) {
488+
BoundLongGauge bound = instrument.bind(attributes);
489+
result.put(attributes, bound::set);
490+
}
491+
}
492+
return result;
493+
case OBSERVABLE_COUNTER:
494+
case OBSERVABLE_UP_DOWN_COUNTER:
495+
case OBSERVABLE_GAUGE:
496+
}
497+
throw new IllegalArgumentException();
498+
}
499+
357500
private interface Instrument {
358501
void record(long value, Attributes attributes);
359502
}
360503

504+
@FunctionalInterface
505+
private interface BoundInstrument {
506+
void record(long value);
507+
}
508+
361509
private static MetricData copy(MetricData m) {
362510
switch (m.getType()) {
363511
case LONG_GAUGE:

0 commit comments

Comments
 (0)