mirror of https://github.com/apache/kafka.git
KAFKA-13846: Adding overloaded metricOrElseCreate method (#12121)
Reviewers: David Jacot <djacot@confluent.io>, Justine Olshan <jolshan@confluent.io>, Guozhang Wang <wangguoz@gmail.com>
This commit is contained in:
parent
4426b05e54
commit
5cab11cf52
|
@ -509,7 +509,10 @@ public class Metrics implements Closeable {
|
|||
Objects.requireNonNull(metricValueProvider),
|
||||
config == null ? this.config : config,
|
||||
time);
|
||||
registerMetric(m);
|
||||
KafkaMetric existingMetric = registerMetric(m);
|
||||
if (existingMetric != null) {
|
||||
throw new IllegalArgumentException("A metric named '" + metricName + "' already exists, can't register another one.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -524,6 +527,26 @@ public class Metrics implements Closeable {
|
|||
addMetric(metricName, null, metricValueProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or get an existing metric to monitor an object that implements MetricValueProvider.
|
||||
* This metric won't be associated with any sensor. This is a way to expose existing values as metrics.
|
||||
* This method takes care of synchronisation while updating/accessing metrics by concurrent threads.
|
||||
*
|
||||
* @param metricName The name of the metric
|
||||
* @param metricValueProvider The metric value provider associated with this metric
|
||||
* @return Existing KafkaMetric if already registered or else a newly created one
|
||||
*/
|
||||
public KafkaMetric addMetricIfAbsent(MetricName metricName, MetricConfig config, MetricValueProvider<?> metricValueProvider) {
|
||||
KafkaMetric metric = new KafkaMetric(new Object(),
|
||||
Objects.requireNonNull(metricName),
|
||||
Objects.requireNonNull(metricValueProvider),
|
||||
config == null ? this.config : config,
|
||||
time);
|
||||
|
||||
KafkaMetric existingMetric = registerMetric(metric);
|
||||
return existingMetric == null ? metric : existingMetric;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a metric if it exists and return it. Return null otherwise. If a metric is removed, `metricRemoval`
|
||||
* will be invoked for each reporter.
|
||||
|
@ -563,10 +586,18 @@ public class Metrics implements Closeable {
|
|||
}
|
||||
}
|
||||
|
||||
synchronized void registerMetric(KafkaMetric metric) {
|
||||
/**
|
||||
* Register a metric if not present or return an already existing metric otherwise.
|
||||
* When a metric is newly registered, this method returns null
|
||||
*
|
||||
* @param metric The KafkaMetric to register
|
||||
* @return KafkaMetric if the metric already exists, null otherwise
|
||||
*/
|
||||
synchronized KafkaMetric registerMetric(KafkaMetric metric) {
|
||||
MetricName metricName = metric.metricName();
|
||||
if (this.metrics.containsKey(metricName))
|
||||
throw new IllegalArgumentException("A metric named '" + metricName + "' already exists, can't register another one.");
|
||||
if (this.metrics.containsKey(metricName)) {
|
||||
return this.metrics.get(metricName);
|
||||
}
|
||||
this.metrics.put(metricName, metric);
|
||||
for (MetricsReporter reporter : reporters) {
|
||||
try {
|
||||
|
@ -576,6 +607,7 @@ public class Metrics implements Closeable {
|
|||
}
|
||||
}
|
||||
log.trace("Registered metric named {}", metricName);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -297,7 +297,10 @@ public final class Sensor {
|
|||
for (NamedMeasurable m : stat.stats()) {
|
||||
final KafkaMetric metric = new KafkaMetric(lock, m.name(), m.stat(), statConfig, time);
|
||||
if (!metrics.containsKey(metric.metricName())) {
|
||||
registry.registerMetric(metric);
|
||||
KafkaMetric existingMetric = registry.registerMetric(metric);
|
||||
if (existingMetric != null) {
|
||||
throw new IllegalArgumentException("A metric named '" + metric.metricName() + "' already exists, can't register another one.");
|
||||
}
|
||||
metrics.put(metric.metricName(), metric);
|
||||
}
|
||||
}
|
||||
|
@ -336,7 +339,10 @@ public final class Sensor {
|
|||
statConfig,
|
||||
time
|
||||
);
|
||||
registry.registerMetric(metric);
|
||||
KafkaMetric existingMetric = registry.registerMetric(metric);
|
||||
if (existingMetric != null) {
|
||||
throw new IllegalArgumentException("A metric named '" + metricName + "' already exists, can't register another one.");
|
||||
}
|
||||
metrics.put(metric.metricName(), metric);
|
||||
stats.add(new StatAndConfig(Objects.requireNonNull(stat), metric::config));
|
||||
return true;
|
||||
|
|
|
@ -319,9 +319,7 @@ public class ConnectMetrics {
|
|||
*/
|
||||
public <T> void addValueMetric(MetricNameTemplate nameTemplate, final LiteralSupplier<T> supplier) {
|
||||
MetricName metricName = metricName(nameTemplate);
|
||||
if (metrics().metric(metricName) == null) {
|
||||
metrics().addMetric(metricName, (Gauge<T>) (config, now) -> supplier.metricValue(now));
|
||||
}
|
||||
metrics().addMetricIfAbsent(metricName, null, (Gauge<T>) (config, now) -> supplier.metricValue(now));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -333,9 +331,7 @@ public class ConnectMetrics {
|
|||
*/
|
||||
public <T> void addImmutableValueMetric(MetricNameTemplate nameTemplate, final T value) {
|
||||
MetricName metricName = metricName(nameTemplate);
|
||||
if (metrics().metric(metricName) == null) {
|
||||
metrics().addMetric(metricName, (Gauge<T>) (config, now) -> value);
|
||||
}
|
||||
metrics().addMetricIfAbsent(metricName, null, (Gauge<T>) (config, now) -> value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -45,6 +45,7 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.apache.kafka.common.utils.Utils.mkEntry;
|
||||
import static org.apache.kafka.common.utils.Utils.mkMap;
|
||||
|
@ -84,6 +85,8 @@ import static org.hamcrest.Matchers.greaterThan;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.powermock.api.easymock.PowerMock.createMock;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
|
@ -497,6 +500,17 @@ public class StreamsMetricsImplTest {
|
|||
verify(metrics);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateNewStoreLevelMutableMetric() {
|
||||
final MetricName metricName =
|
||||
new MetricName(METRIC_NAME1, STATE_STORE_LEVEL_GROUP, DESCRIPTION1, STORE_LEVEL_TAG_MAP);
|
||||
final MetricConfig metricConfig = new MetricConfig().recordLevel(INFO_RECORDING_LEVEL);
|
||||
final Metrics metrics = new Metrics(metricConfig);
|
||||
assertNull(metrics.metric(metricName));
|
||||
metrics.addMetricIfAbsent(metricName, metricConfig, VALUE_PROVIDER);
|
||||
assertNotNull(metrics.metric(metricName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotAddStoreLevelMutableMetricIfAlreadyExists() {
|
||||
final Metrics metrics = mock(Metrics.class);
|
||||
|
@ -521,6 +535,38 @@ public class StreamsMetricsImplTest {
|
|||
verify(metrics);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnSameMetricIfAlreadyCreated() {
|
||||
final MetricName metricName =
|
||||
new MetricName(METRIC_NAME1, STATE_STORE_LEVEL_GROUP, DESCRIPTION1, STORE_LEVEL_TAG_MAP);
|
||||
final MetricConfig metricConfig = new MetricConfig().recordLevel(INFO_RECORDING_LEVEL);
|
||||
final Metrics metrics = new Metrics(metricConfig);
|
||||
assertNull(metrics.metric(metricName));
|
||||
final KafkaMetric kafkaMetric = metrics.addMetricIfAbsent(metricName, metricConfig, VALUE_PROVIDER);
|
||||
assertEquals(kafkaMetric, metrics.addMetricIfAbsent(metricName, metricConfig, VALUE_PROVIDER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateMetricOnceDuringConcurrentMetricCreationRequest() throws InterruptedException {
|
||||
final MetricName metricName =
|
||||
new MetricName(METRIC_NAME1, STATE_STORE_LEVEL_GROUP, DESCRIPTION1, STORE_LEVEL_TAG_MAP);
|
||||
final MetricConfig metricConfig = new MetricConfig().recordLevel(INFO_RECORDING_LEVEL);
|
||||
final Metrics metrics = new Metrics(metricConfig);
|
||||
assertNull(metrics.metric(metricName));
|
||||
final AtomicReference<KafkaMetric> metricCreatedViaThread1 = new AtomicReference<>();
|
||||
final AtomicReference<KafkaMetric> metricCreatedViaThread2 = new AtomicReference<>();
|
||||
|
||||
final Thread thread1 = new Thread(() -> metricCreatedViaThread1.set(metrics.addMetricIfAbsent(metricName, metricConfig, VALUE_PROVIDER)));
|
||||
final Thread thread2 = new Thread(() -> metricCreatedViaThread2.set(metrics.addMetricIfAbsent(metricName, metricConfig, VALUE_PROVIDER)));
|
||||
|
||||
thread1.start();
|
||||
thread2.start();
|
||||
|
||||
thread1.join();
|
||||
thread2.join();
|
||||
assertEquals(metricCreatedViaThread1.get(), metricCreatedViaThread2.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRemoveStateStoreLevelSensors() {
|
||||
final Metrics metrics = niceMock(Metrics.class);
|
||||
|
|
Loading…
Reference in New Issue