Make InMemoryMetricRepository.increment() thread safe

This commit is contained in:
Dave Syer 2013-09-08 13:11:56 +01:00
parent eb246d6bed
commit 7a6131c466
2 changed files with 65 additions and 16 deletions

View File

@ -31,18 +31,26 @@ public class InMemoryMetricRepository implements MetricRepository {
private ConcurrentMap<String, Measurement> metrics = new ConcurrentHashMap<String, Measurement>(); private ConcurrentMap<String, Measurement> metrics = new ConcurrentHashMap<String, Measurement>();
private ConcurrentMap<String, Object> locks = new ConcurrentHashMap<String, Object>();
@Override @Override
public void increment(String metricName, int amount, Date timestamp) { public void increment(String metricName, int amount, Date timestamp) {
Measurement current = this.metrics.get(metricName); Measurement current = this.metrics.get(metricName);
if (current != null) { if (current != null) {
Metric metric = current.getMetric(); Object lock = this.locks.putIfAbsent(metricName, new Object());
this.metrics.replace(metricName, current, if (lock == null) {
new Measurement(timestamp, metric.increment(amount))); lock = this.locks.get(metricName);
} }
else { synchronized (lock) {
this.metrics.putIfAbsent(metricName, new Measurement(timestamp, new Metric( current = this.metrics.get(metricName);
metricName, amount))); Metric metric = current.getMetric();
this.metrics.replace(metricName, current, new Measurement(timestamp,
metric.increment(amount)));
return;
}
} }
this.metrics.putIfAbsent(metricName, new Measurement(timestamp, new Metric(
metricName, amount)));
} }
@Override @Override

View File

@ -16,24 +16,65 @@
package org.springframework.boot.actuate.metrics; package org.springframework.boot.actuate.metrics;
import org.junit.Ignore; import java.util.ArrayList;
import org.junit.Test; import java.util.Collection;
import org.springframework.boot.actuate.metrics.InMemoryMetricRepository; import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.fail; import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/** /**
* Tests for {@link InMemoryMetricRepository}. * Tests for {@link InMemoryMetricRepository}.
*/ */
@Ignore
public class InMemoryMetricRepositoryTests { public class InMemoryMetricRepositoryTests {
// FIXME write tests private InMemoryMetricRepository repository = new InMemoryMetricRepository();
// FIXME possibly also add Metric/Measurement tests
@Test @Test
public void test() { public void increment() {
fail("Not yet implemented"); this.repository.increment("foo", 1, new Date());
assertEquals(1.0, this.repository.findOne("foo").getValue(), 0.01);
}
@Test
public void incrementConcurrent() throws Exception {
Collection<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>();
for (int i = 0; i < 100; i++) {
tasks.add(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
InMemoryMetricRepositoryTests.this.repository.increment("foo", 1,
new Date());
return true;
}
});
tasks.add(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
InMemoryMetricRepositoryTests.this.repository.increment("foo", -1,
new Date());
return true;
}
});
}
List<Future<Boolean>> all = Executors.newFixedThreadPool(10).invokeAll(tasks);
for (Future<Boolean> future : all) {
assertTrue(future.get(1, TimeUnit.SECONDS));
}
assertEquals(0, this.repository.findOne("foo").getValue(), 0.01);
}
@Test
public void set() {
this.repository.set("foo", 1, new Date());
assertEquals(1.0, this.repository.findOne("foo").getValue(), 0.01);
} }
} }