Skip to content

Commit 12548ef

Browse files
Ignore noop children when polling composite meters
When a CompositeMeterRegistry is used and one of its CompositeMeters is polled (its value is fetched), the returned value can depend on the order of the registries inside of the composite if the composite contains a registry that has any NoopMeters. Example: a CompositeMeterRegistry contains two registries, A and B. We create a counter in the composite and increment it once. After this both A and B contain one counter but lets say that in A the counter is ignored so it will be noop. When the count called on CompositeCounter, it can return either 0 (if NoopCounter was used) or 1 (if the non-noop counter was used). In order to fix this, we can ignore the NoopMeters when Meters are polled in a composite registry. Closes micrometer-metricsgh-1441
1 parent 0da6415 commit 12548ef

File tree

2 files changed

+38
-4
lines changed

2 files changed

+38
-4
lines changed

Diff for: micrometer-core/src/main/java/io/micrometer/core/instrument/composite/AbstractCompositeMeter.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
import io.micrometer.core.instrument.AbstractMeter;
1919
import io.micrometer.core.instrument.Meter;
2020
import io.micrometer.core.instrument.MeterRegistry;
21+
import io.micrometer.core.instrument.noop.NoopMeter;
2122
import io.micrometer.core.lang.Nullable;
2223

2324
import java.util.Collections;
2425
import java.util.IdentityHashMap;
25-
import java.util.Iterator;
2626
import java.util.Map;
2727
import java.util.concurrent.atomic.AtomicBoolean;
2828

@@ -49,9 +49,11 @@ final Iterable<T> getChildren() {
4949
}
5050

5151
T firstChild() {
52-
final Iterator<T> i = children.values().iterator();
53-
if (i.hasNext())
54-
return i.next();
52+
for (T next : children.values()) {
53+
if (!(next instanceof NoopMeter)) {
54+
return next;
55+
}
56+
}
5557

5658
// There are no child meters. Return a lazily instantiated no-op meter.
5759
final T noopMeter = this.noopMeter;

Diff for: micrometer-core/src/test/java/io/micrometer/core/instrument/composite/CompositeMeterRegistryTest.java

+32
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,23 @@
2828
import org.junit.jupiter.api.DisplayName;
2929
import org.junit.jupiter.api.RepeatedTest;
3030
import org.junit.jupiter.api.Test;
31+
import org.junit.jupiter.params.ParameterizedTest;
32+
import org.junit.jupiter.params.provider.MethodSource;
3133
import reactor.core.publisher.Flux;
3234
import reactor.core.scheduler.Schedulers;
3335

3436
import java.time.Duration;
3537
import java.util.HashMap;
38+
import java.util.List;
3639
import java.util.Map;
3740
import java.util.concurrent.CountDownLatch;
3841
import java.util.concurrent.ExecutorService;
3942
import java.util.concurrent.Executors;
4043
import java.util.concurrent.TimeUnit;
4144
import java.util.concurrent.atomic.AtomicInteger;
45+
import java.util.stream.Stream;
4246

47+
import static java.util.Arrays.asList;
4348
import static java.util.Collections.emptyList;
4449
import static java.util.Collections.singletonList;
4550
import static org.assertj.core.api.Assertions.assertThat;
@@ -493,4 +498,31 @@ void meterRemovalPropagatesToChildRegistryWithModifyingFilter() {
493498
assertThat(this.simple.getMeters()).isEmpty();
494499
}
495500

501+
@Issue("#1441")
502+
@ParameterizedTest
503+
@MethodSource("registriesProvider")
504+
void whenMetersArePolledNoopChildrenShouldBeIgnored(List<MeterRegistry> registries) {
505+
// this means that firstChild() practically should be firstNonNoopChild()
506+
CompositeMeterRegistry composite = new CompositeMeterRegistry(Clock.SYSTEM, registries);
507+
Counter counter = composite.counter("my.counter");
508+
counter.increment();
509+
assertThat(counter.count()).isEqualTo(1);
510+
}
511+
512+
static Stream<List<MeterRegistry>> registriesProvider() {
513+
// Since the order is non-deterministic, the best effort is testing both orders
514+
SimpleMeterRegistry denyAllRegistry1 = new SimpleMeterRegistry();
515+
denyAllRegistry1.config().meterFilter(MeterFilter.deny());
516+
517+
SimpleMeterRegistry denyAllRegistry2 = new SimpleMeterRegistry();
518+
denyAllRegistry2.config().meterFilter(MeterFilter.deny());
519+
520+
// @formatter:off
521+
return Stream.of(
522+
asList(denyAllRegistry1, new SimpleMeterRegistry()), // denyAll, allowAll
523+
asList(new SimpleMeterRegistry(), denyAllRegistry2) // allowAll, denyAll
524+
);
525+
// @formatter:on
526+
}
527+
496528
}

0 commit comments

Comments
 (0)