Skip to content

Commit

Permalink
OAK-6593: CacheStats metrics
Browse files Browse the repository at this point in the history
git-svn-id: https://svn.apache.org/repos/asf/jackrabbit/oak/trunk@1806432 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
mreutegg committed Aug 28, 2017
1 parent 0d83341 commit c36536c
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 0 deletions.
5 changes: 5 additions & 0 deletions oak-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@
<artifactId>org.osgi.annotation</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.component.annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.jackrabbit.oak.cache.impl;

import java.util.List;
import java.util.Map;

import com.codahale.metrics.Counter;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricRegistry;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;

import org.apache.jackrabbit.oak.api.jmx.CacheStatsMBean;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* An OSGi component that binds to all {@link CacheStatsMBean} instances and
* exposes their counters as {@link Metric}s.
*/
@Component(immediate = true)
public class CacheStatsMetrics {

private static final Logger LOG = LoggerFactory.getLogger(CacheStatsMetrics.class);

static final String HIT = "hit";
static final String MISS = "miss";
static final String EVICTION = "eviction";
static final String ELEMENT = "element";
static final String LOAD_TIME = "loadTime";

private static final List<String> TYPES = ImmutableList.of(
HIT, MISS, EVICTION, ELEMENT, LOAD_TIME);

private Map<String, CacheStatsMBean> cacheStatsMBeans = Maps.newHashMap();
private MetricRegistry registry = new MetricRegistry();

@Reference(
cardinality = ReferenceCardinality.AT_LEAST_ONE,
policy = ReferencePolicy.DYNAMIC
)
synchronized void addCacheStatsMBean(CacheStatsMBean stats) {
LOG.debug("addCacheStatsMBean({})", stats.getName());
cacheStatsMBeans.put(stats.getName(), stats);
registerCacheStatsMBean(this.registry, stats);
}

synchronized void removeCacheStatsMBean(CacheStatsMBean stats) {
LOG.debug("removeCacheStatsMBean({})", stats.getName());
cacheStatsMBeans.remove(stats.getName());
unregisterCacheStatsMBean(this.registry, stats);
}

@Reference(
target = "(name=oak)"
)
synchronized void setMetricRegistry(MetricRegistry registry) {
LOG.debug("setMetricRegistry({})", registry);
for (CacheStatsMBean stats : cacheStatsMBeans.values()) {
unregisterCacheStatsMBean(this.registry, stats);
registerCacheStatsMBean(registry, stats);
}
this.registry = registry;
}

private static void registerCacheStatsMBean(MetricRegistry registry,
CacheStatsMBean stats) {
registerMetric(registry, new HitCounter(stats));
registerMetric(registry, new MissCounter(stats));
registerMetric(registry, new EvictionCounter(stats));
registerMetric(registry, new ElementCounter(stats));
registerMetric(registry, new LoadTimeCounter(stats));
}

private static void registerMetric(MetricRegistry registry,
CacheStatsMBeanCounter metric) {
String name = metric.getName();
registry.remove(name);
registry.register(name, metric);
}

private static void unregisterCacheStatsMBean(MetricRegistry registry,
CacheStatsMBean stats) {
String name = stats.getName();
for (String t : TYPES) {
registry.remove(metricName(name, t));
}
}

static String metricName(String cacheStatsName, String type) {
return "CacheStats." + cacheStatsName + "." + type;
}

private abstract static class CacheStatsMBeanCounter
extends Counter {

protected CacheStatsMBean stats;
protected String type;

CacheStatsMBeanCounter(CacheStatsMBean stats, String type) {
this.stats = stats;
this.type = type;
}

String getName() {
return metricName(stats.getName(), type);
}
}

private static final class HitCounter extends CacheStatsMBeanCounter {

HitCounter(CacheStatsMBean stats) {
super(stats, HIT);
}

@Override
public long getCount() {
return stats.getHitCount();
}
}

private static final class MissCounter extends CacheStatsMBeanCounter {

MissCounter(CacheStatsMBean stats) {
super(stats, MISS);
}

@Override
public long getCount() {
return stats.getMissCount();
}
}

private static final class EvictionCounter extends CacheStatsMBeanCounter {

EvictionCounter(CacheStatsMBean stats) {
super(stats, EVICTION);
}

@Override
public long getCount() {
return stats.getEvictionCount();
}
}

private static final class ElementCounter extends CacheStatsMBeanCounter {

ElementCounter(CacheStatsMBean stats) {
super(stats, ELEMENT);
}

@Override
public long getCount() {
return stats.getElementCount();
}
}

private static final class LoadTimeCounter extends CacheStatsMBeanCounter {

LoadTimeCounter(CacheStatsMBean stats) {
super(stats, LOAD_TIME);
}

@Override
public long getCount() {
return stats.getTotalLoadTime();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.jackrabbit.oak.cache.impl;

import java.util.Map;
import java.util.Random;

import javax.annotation.Nonnull;

import com.codahale.metrics.Counter;
import com.codahale.metrics.MetricRegistry;

import org.apache.jackrabbit.oak.api.jmx.CacheStatsMBean;
import org.apache.jackrabbit.oak.cache.AbstractCacheStats;
import org.junit.Test;

import static org.apache.jackrabbit.oak.cache.impl.CacheStatsMetrics.ELEMENT;
import static org.apache.jackrabbit.oak.cache.impl.CacheStatsMetrics.EVICTION;
import static org.apache.jackrabbit.oak.cache.impl.CacheStatsMetrics.HIT;
import static org.apache.jackrabbit.oak.cache.impl.CacheStatsMetrics.MISS;
import static org.apache.jackrabbit.oak.cache.impl.CacheStatsMetrics.metricName;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

public class CacheStatsMetricsTest {

private static final Random RANDOM = new Random();
private static final long HIT_COUNT = RANDOM.nextInt(Integer.MAX_VALUE);
private static final long MISS_COUNT = RANDOM.nextInt(Integer.MAX_VALUE);
private static final long EVICTION_COUNT = RANDOM.nextInt(Integer.MAX_VALUE);
private static final long ELEMENT_COUNT = RANDOM.nextInt(Integer.MAX_VALUE);
private static final long LOAD_TIME = RANDOM.nextInt(Integer.MAX_VALUE);

@Test
public void metrics() {
MetricRegistry registry = new MetricRegistry();
CacheStatsMetrics metrics = new CacheStatsMetrics();
metrics.setMetricRegistry(registry);

CacheStatsMBean bean = new TestStats("stats");
metrics.addCacheStatsMBean(bean);

Map<String, Counter> counters = registry.getCounters();

Counter counter = counters.get(metricName(bean.getName(), HIT));
assertNotNull(counter);
assertEquals(HIT_COUNT, counter.getCount());

counter = counters.get(metricName(bean.getName(), MISS));
assertNotNull(counter);
assertEquals(MISS_COUNT, counter.getCount());

counter = counters.get(metricName(bean.getName(), EVICTION));
assertNotNull(counter);
assertEquals(EVICTION_COUNT, counter.getCount());

counter = counters.get(metricName(bean.getName(), ELEMENT));
assertNotNull(counter);
assertEquals(ELEMENT_COUNT, counter.getCount());

counter = counters.get(metricName(bean.getName(), CacheStatsMetrics.LOAD_TIME));
assertNotNull(counter);
assertEquals(LOAD_TIME, counter.getCount());

metrics.removeCacheStatsMBean(bean);
assertEquals(0, registry.getCounters().size());
}

private static class TestStats extends AbstractCacheStats {

TestStats(@Nonnull String name) {
super(name);
}

@Override
protected com.google.common.cache.CacheStats getCurrentStats() {
return new com.google.common.cache.CacheStats(
HIT_COUNT, MISS_COUNT, MISS_COUNT, 0, LOAD_TIME, EVICTION_COUNT);
}

@Override
public long getElementCount() {
return ELEMENT_COUNT;
}

@Override
public long getMaxTotalWeight() {
return 1000;
}

@Override
public long estimateCurrentWeight() {
return 1000;
}
}
}
5 changes: 5 additions & 0 deletions oak-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,11 @@
<artifactId>org.osgi.compendium</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.component.annotations</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
Expand Down

0 comments on commit c36536c

Please sign in to comment.