[AB-353] reducing chance of concurrent modification exception when registering a new metric on the fly

This commit is contained in:
Sheldan
2021-12-25 10:53:55 +01:00
parent 9ce07a1a4a
commit cc898b27bb
2 changed files with 49 additions and 41 deletions

View File

@@ -33,9 +33,9 @@ public class JDAMetrics extends ListenerAdapter {
.tagList(Arrays.asList(MetricTag.getTag(EVENT_CLASS, eventName))) .tagList(Arrays.asList(MetricTag.getTag(EVENT_CLASS, eventName)))
.name(JDA_EVENT_METRIC) .name(JDA_EVENT_METRIC)
.build(); .build();
log.info("Registering new metric for event {}. There are now {} metrics.", eventName, coveredEvents.size());
metricService.registerCounter(metric, "Events of type " + eventName); metricService.registerCounter(metric, "Events of type " + eventName);
coveredEvents.put(event.getClass(), metric); coveredEvents.put(event.getClass(), metric);
log.info("Registering new metric for event {}. There are now {} metrics.", eventName, coveredEvents.size());
} }
metricService.incrementCounter(coveredEvents.get(event.getClass())); metricService.incrementCounter(coveredEvents.get(event.getClass()));
} }

View File

@@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -15,14 +16,15 @@ import java.util.function.ToDoubleFunction;
@Slf4j @Slf4j
public class PrometheusMetricServiceBean implements MetricService { public class PrometheusMetricServiceBean implements MetricService {
private final List<Counter> counters = new ArrayList<>(); private final List<Counter> counters = Collections.synchronizedList(new ArrayList<>());
private final List<Gauge> gauges = new ArrayList<>(); private final List<Gauge> gauges = Collections.synchronizedList(new ArrayList<>());
@Autowired @Autowired
private MeterRegistry registry; private MeterRegistry registry;
@Override @Override
public void registerCounter(CounterMetric metric, String description) { public void registerCounter(CounterMetric metric, String description) {
synchronized (counters) {
if(doesCounterExist(metric)) { if(doesCounterExist(metric)) {
throw new IllegalArgumentException("Counter metric already exists."); throw new IllegalArgumentException("Counter metric already exists.");
} }
@@ -36,6 +38,7 @@ public class PrometheusMetricServiceBean implements MetricService {
.register(registry); .register(registry);
counters.add(counter); counters.add(counter);
} }
}
@Override @Override
public void registerGauge(CounterMetric counterMetric, Supplier<Number> f, String help) { public void registerGauge(CounterMetric counterMetric, Supplier<Number> f, String help) {
@@ -44,11 +47,11 @@ public class PrometheusMetricServiceBean implements MetricService {
@Override @Override
public void registerGauge(CounterMetric counterMetric, Supplier<Number> f, String help, String baseUnit) { public void registerGauge(CounterMetric counterMetric, Supplier<Number> f, String help, String baseUnit) {
synchronized (gauges) {
List<Tag> micrometerTags = new ArrayList<>(); List<Tag> micrometerTags = new ArrayList<>();
counterMetric.getTagList().forEach(metricTag -> counterMetric.getTagList().forEach(metricTag ->
micrometerTags.add(Tag.of(metricTag.getKey(), metricTag.getValue())) micrometerTags.add(Tag.of(metricTag.getKey(), metricTag.getValue()))
); );
Gauge gauge = Gauge Gauge gauge = Gauge
.builder(counterMetric.getName(), f) .builder(counterMetric.getName(), f)
.tags(micrometerTags) .tags(micrometerTags)
@@ -58,6 +61,8 @@ public class PrometheusMetricServiceBean implements MetricService {
gauges.add(gauge); gauges.add(gauge);
} }
}
@Override @Override
public <T> void registerGauge(CounterMetric counterMetric, T obj, ToDoubleFunction<T> f, String help) { public <T> void registerGauge(CounterMetric counterMetric, T obj, ToDoubleFunction<T> f, String help) {
registerGauge(counterMetric, obj, f, help, null); registerGauge(counterMetric, obj, f, help, null);
@@ -65,11 +70,11 @@ public class PrometheusMetricServiceBean implements MetricService {
@Override @Override
public <T> void registerGauge(CounterMetric counterMetric, T obj, ToDoubleFunction<T> f, String help, String baseUnit) { public <T> void registerGauge(CounterMetric counterMetric, T obj, ToDoubleFunction<T> f, String help, String baseUnit) {
synchronized (gauges) {
List<Tag> micrometerTags = new ArrayList<>(); List<Tag> micrometerTags = new ArrayList<>();
counterMetric.getTagList().forEach(metricTag -> counterMetric.getTagList().forEach(metricTag ->
micrometerTags.add(Tag.of(metricTag.getKey(), metricTag.getValue())) micrometerTags.add(Tag.of(metricTag.getKey(), metricTag.getValue()))
); );
Gauge gauge = Gauge Gauge gauge = Gauge
.builder(counterMetric.getName(), obj, f) .builder(counterMetric.getName(), obj, f)
.tags(micrometerTags) .tags(micrometerTags)
@@ -78,6 +83,7 @@ public class PrometheusMetricServiceBean implements MetricService {
.register(registry); .register(registry);
gauges.add(gauge); gauges.add(gauge);
} }
}
@Override @Override
public void incrementCounter(CounterMetric counterMetric) { public void incrementCounter(CounterMetric counterMetric) {
@@ -86,12 +92,14 @@ public class PrometheusMetricServiceBean implements MetricService {
@Override @Override
public void incrementCounter(CounterMetric counterMetric, Long amount) { public void incrementCounter(CounterMetric counterMetric, Long amount) {
synchronized (counters) {
Optional<Counter> counterOptional = counters.stream().filter(counter -> compareCounterIdAndCounterMetric(counter.getId(), counterMetric)).findFirst(); Optional<Counter> counterOptional = counters.stream().filter(counter -> compareCounterIdAndCounterMetric(counter.getId(), counterMetric)).findFirst();
counterOptional.ifPresent(counter -> counter.increment(amount)); counterOptional.ifPresent(counter -> counter.increment(amount));
if(!counterOptional.isPresent()) { if(!counterOptional.isPresent()) {
log.warn("Trying to increment counter {} with tags {}, which was not available (yet).", counterMetric.getName(), String.join(counterMetric.getTagList().toString())); log.warn("Trying to increment counter {} with tags {}, which was not available (yet).", counterMetric.getName(), String.join(counterMetric.getTagList().toString()));
} }
} }
}
private boolean doesCounterExist(CounterMetric counterMetric) { private boolean doesCounterExist(CounterMetric counterMetric) {
return counters.stream().anyMatch(counter -> compareCounterIdAndCounterMetric(counter.getId(), counterMetric)); return counters.stream().anyMatch(counter -> compareCounterIdAndCounterMetric(counter.getId(), counterMetric));