mirror of https://github.com/apache/cassandra
345 lines
18 KiB
Java
345 lines
18 KiB
Java
/*
|
|
* 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.cassandra.metrics;
|
|
|
|
import java.util.EnumMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.stream.Collectors;
|
|
import javax.management.JMX;
|
|
import javax.management.ObjectName;
|
|
|
|
import org.junit.Before;
|
|
import org.junit.BeforeClass;
|
|
import org.junit.Test;
|
|
|
|
import com.codahale.metrics.Metric;
|
|
import com.codahale.metrics.MetricRegistry;
|
|
import org.apache.cassandra.cql3.CQLTester;
|
|
import org.apache.cassandra.db.virtual.CollectionVirtualTableAdapter;
|
|
|
|
import static java.util.Objects.requireNonNull;
|
|
import static org.apache.cassandra.metrics.CassandraMetricsRegistry.METRICS_GROUP_POSTFIX;
|
|
import static org.apache.cassandra.schema.SchemaConstants.VIRTUAL_METRICS;
|
|
import static org.junit.Assert.assertEquals;
|
|
import static org.junit.Assert.assertNotNull;
|
|
|
|
/**
|
|
* Test compares JMX metrics to virtual table metrics values, basically all metric values must be equal and
|
|
* have the same representation in both places.
|
|
*/
|
|
public class JmxVirtualTableMetricsTest extends CQLTester
|
|
{
|
|
private final Map<MetricType, Metric> metricToNameMap = new EnumMap<>(MetricType.class);
|
|
private final AtomicInteger gaugeValue = new AtomicInteger(123);
|
|
|
|
@BeforeClass
|
|
public static void setup() throws Exception
|
|
{
|
|
startJMXServer();
|
|
createMBeanServerConnection();
|
|
addMetricsKeyspace();
|
|
}
|
|
|
|
@Before
|
|
public void beforeTest()
|
|
{
|
|
metricToNameMap.clear();
|
|
MetricRegistry registry = new MetricRegistry();
|
|
|
|
metricToNameMap.put(MetricType.METER, registry.meter("meter"));
|
|
metricToNameMap.put(MetricType.COUNTER, registry.counter("counter"));
|
|
metricToNameMap.put(MetricType.HISTOGRAM, registry.histogram("histogram", () -> new CassandraHistogram(new DecayingEstimatedHistogramReservoir(true))));
|
|
metricToNameMap.put(MetricType.TIMER, registry.timer("timer"));
|
|
metricToNameMap.put(MetricType.GAUGE, registry.gauge("gauge", () -> gaugeValue::get));
|
|
|
|
CassandraMetricsRegistry.metricGroups.forEach(group -> {
|
|
MetricNameFactory factory = new DefaultNameFactory(group, "jmx.virtual");
|
|
CassandraMetricsRegistry.Metrics.register(factory.createMetricName(MetricType.METER.metricName),
|
|
metricToNameMap.get(MetricType.METER));
|
|
CassandraMetricsRegistry.Metrics.register(factory.createMetricName(MetricType.COUNTER.metricName),
|
|
metricToNameMap.get(MetricType.COUNTER));
|
|
CassandraMetricsRegistry.Metrics.register(factory.createMetricName(MetricType.HISTOGRAM.metricName),
|
|
metricToNameMap.get(MetricType.HISTOGRAM));
|
|
CassandraMetricsRegistry.Metrics.register(factory.createMetricName(MetricType.TIMER.metricName),
|
|
metricToNameMap.get(MetricType.TIMER));
|
|
CassandraMetricsRegistry.Metrics.register(factory.createMetricName(MetricType.GAUGE.metricName),
|
|
metricToNameMap.get(MetricType.GAUGE));
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void testJmxEqualVirtualTableByMetricGroup() throws Exception
|
|
{
|
|
Map<String, List<ObjectName>> mbeanByMetricGroup = jmxConnection.queryNames(null, null)
|
|
.stream()
|
|
.filter(this::isLocalMetric)
|
|
.collect(Collectors.groupingBy(
|
|
on -> requireNonNull(
|
|
on.getKeyPropertyList().get("type"))));
|
|
|
|
for (Map.Entry<String, List<ObjectName>> e : mbeanByMetricGroup.entrySet())
|
|
{
|
|
assertRowsContains(executeNet(String.format("SELECT * FROM %s.%s", VIRTUAL_METRICS,
|
|
METRICS_GROUP_POSTFIX.apply(
|
|
CollectionVirtualTableAdapter.virtualTableNameStyle(
|
|
e.getKey())))),
|
|
e.getValue().stream().map(this::makeMetricRow).collect(Collectors.toList()));
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testJmxEqualVirtualTableByMetricType() throws Exception
|
|
{
|
|
Map<MetricType, List<ObjectName>> mbeanByMetricGroup = jmxConnection.queryNames(null, null)
|
|
.stream()
|
|
.filter(this::isLocalMetric)
|
|
.collect(Collectors.groupingBy(
|
|
on -> MetricType.find(
|
|
on.getKeyPropertyList().get("name"))
|
|
.orElseThrow()));
|
|
|
|
for (Map.Entry<MetricType, List<ObjectName>> e : mbeanByMetricGroup.entrySet())
|
|
{
|
|
switch (e.getKey())
|
|
{
|
|
case METER:
|
|
assertRowsContains(executeNet(String.format("SELECT * FROM %s.type_meter", VIRTUAL_METRICS)),
|
|
e.getValue().stream().map(this::makeMeterRow).collect(Collectors.toList()));
|
|
break;
|
|
case COUNTER:
|
|
assertRowsContains(executeNet(String.format("SELECT * FROM %s.type_counter", VIRTUAL_METRICS)),
|
|
e.getValue().stream().map(this::makeCounterRow).collect(Collectors.toList()));
|
|
break;
|
|
case HISTOGRAM:
|
|
assertRowsContains(executeNet(String.format("SELECT * FROM %s.type_histogram", VIRTUAL_METRICS)),
|
|
e.getValue().stream().map(this::makeHistogramRow).collect(Collectors.toList()));
|
|
break;
|
|
case TIMER:
|
|
assertRowsContains(executeNet(String.format("SELECT * FROM %s.type_timer", VIRTUAL_METRICS)),
|
|
e.getValue().stream().map(this::makeTimerRow).collect(Collectors.toList()));
|
|
break;
|
|
case GAUGE:
|
|
assertRowsContains(executeNet(String.format("SELECT * FROM %s.type_gauge", VIRTUAL_METRICS)),
|
|
e.getValue().stream().map(this::makeGaugeRow).collect(Collectors.toList()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testAliasesWithJmxVirtualTables() throws Exception
|
|
{
|
|
CassandraMetricsRegistry registry = CassandraMetricsRegistry.Metrics;
|
|
MetricNameFactory factory = new DefaultNameFactory("Table", "AliasTestScopeFirst");
|
|
MetricNameFactory aliasFactory = new DefaultNameFactory("Index", "AliasTestScopeSecond");
|
|
|
|
CassandraMetricsRegistry.MetricName meter = factory.createMetricName("TestMeter");
|
|
CassandraMetricsRegistry.MetricName meterAlias = aliasFactory.createMetricName("TestMeterAlias");
|
|
registry.meter(meter, meterAlias);
|
|
|
|
assertRowsContains(executeNet(String.format("SELECT * FROM %s.type_meter", VIRTUAL_METRICS)),
|
|
row(meter.getMetricName(), 0L, 0.0, 0.0, 0.0, 0.0, "AliasTestScopeFirst"),
|
|
row(meterAlias.getMetricName(), 0L, 0.0, 0.0, 0.0, 0.0, "AliasTestScopeSecond"));
|
|
|
|
CassandraMetricsRegistry.JmxMeterMBean bean = JMX.newMBeanProxy(jmxConnection,
|
|
meter.getMBeanName(),
|
|
CassandraMetricsRegistry.JmxMeterMBean.class);
|
|
|
|
CassandraMetricsRegistry.JmxMeterMBean beanAlias = JMX.newMBeanProxy(jmxConnection,
|
|
meterAlias.getMBeanName(),
|
|
CassandraMetricsRegistry.JmxMeterMBean.class);
|
|
|
|
assertNotNull(bean);
|
|
assertNotNull(beanAlias);
|
|
assertEquals(bean.getCount(), beanAlias.getCount());
|
|
assertEquals(bean.getFifteenMinuteRate(), beanAlias.getFifteenMinuteRate(), 0.0);
|
|
assertEquals(bean.getFiveMinuteRate(), beanAlias.getFiveMinuteRate(), 0.0);
|
|
assertEquals(bean.getMeanRate(), beanAlias.getMeanRate(), 0.0);
|
|
assertEquals(bean.getOneMinuteRate(), beanAlias.getOneMinuteRate(), 0.0);
|
|
}
|
|
|
|
private Object[] makeMetricRow(ObjectName objectName)
|
|
{
|
|
MetricType type = MetricType.find(objectName.getKeyPropertyList().get("name")).orElseThrow();
|
|
String jmxValue;
|
|
switch (type)
|
|
{
|
|
case METER:
|
|
jmxValue = String.valueOf(JMX.newMBeanProxy(jmxConnection,
|
|
objectName,
|
|
CassandraMetricsRegistry.JmxMeterMBean.class).getCount());
|
|
break;
|
|
case COUNTER:
|
|
jmxValue = String.valueOf(JMX.newMBeanProxy(jmxConnection,
|
|
objectName,
|
|
CassandraMetricsRegistry.JmxCounterMBean.class).getCount());
|
|
break;
|
|
case HISTOGRAM:
|
|
jmxValue = String.valueOf(JMX.newMBeanProxy(jmxConnection,
|
|
objectName,
|
|
CassandraMetricsRegistry.JmxHistogramMBean.class).get50thPercentile());
|
|
break;
|
|
case TIMER:
|
|
jmxValue = String.valueOf(JMX.newMBeanProxy(jmxConnection,
|
|
objectName,
|
|
CassandraMetricsRegistry.JmxTimerMBean.class).getCount());
|
|
break;
|
|
case GAUGE:
|
|
jmxValue = String.valueOf(JMX.newMBeanProxy(jmxConnection,
|
|
objectName,
|
|
CassandraMetricsRegistry.JmxGaugeMBean.class).getValue());
|
|
break;
|
|
default:
|
|
throw new RuntimeException("Unknown metric type: " + objectName.getKeyPropertyList().get("name"));
|
|
}
|
|
return CQLTester.row(getFullMetricName(objectName),
|
|
requireNonNull(objectName.getKeyPropertyList().get("scope")),
|
|
MetricType.find(objectName.getKeyPropertyList().get("name"))
|
|
.map(MetricType::name)
|
|
.map(String::toLowerCase)
|
|
.orElseThrow(),
|
|
jmxValue);
|
|
}
|
|
|
|
private Object[] makeMeterRow(ObjectName objectName)
|
|
{
|
|
assertEquals(MetricType.METER.metricName, objectName.getKeyPropertyList().get("name"));
|
|
CassandraMetricsRegistry.JmxMeterMBean bean = JMX.newMBeanProxy(jmxConnection,
|
|
objectName,
|
|
CassandraMetricsRegistry.JmxMeterMBean.class);
|
|
|
|
return CQLTester.row(getFullMetricName(objectName),
|
|
bean.getCount(),
|
|
bean.getFifteenMinuteRate(),
|
|
bean.getFiveMinuteRate(),
|
|
bean.getMeanRate(),
|
|
bean.getOneMinuteRate(),
|
|
requireNonNull(objectName.getKeyPropertyList().get("scope")));
|
|
}
|
|
|
|
private Object[] makeCounterRow(ObjectName objectName)
|
|
{
|
|
assertEquals(MetricType.COUNTER.metricName, objectName.getKeyPropertyList().get("name"));
|
|
CassandraMetricsRegistry.JmxCounterMBean bean = JMX.newMBeanProxy(jmxConnection,
|
|
objectName,
|
|
CassandraMetricsRegistry.JmxCounterMBean.class);
|
|
|
|
return CQLTester.row(getFullMetricName(objectName),
|
|
requireNonNull(objectName.getKeyPropertyList().get("scope")),
|
|
bean.getCount());
|
|
}
|
|
|
|
private Object[] makeHistogramRow(ObjectName objectName)
|
|
{
|
|
assertEquals(MetricType.HISTOGRAM.metricName, objectName.getKeyPropertyList().get("name"));
|
|
CassandraMetricsRegistry.JmxHistogramMBean bean = JMX.newMBeanProxy(jmxConnection,
|
|
objectName,
|
|
CassandraMetricsRegistry.JmxHistogramMBean.class);
|
|
|
|
return CQLTester.row(getFullMetricName(objectName),
|
|
bean.getMax(),
|
|
bean.getMean(),
|
|
bean.getMin(),
|
|
bean.get75thPercentile(),
|
|
bean.get95thPercentile(),
|
|
bean.get98thPercentile(),
|
|
bean.get999thPercentile(),
|
|
bean.get99thPercentile(),
|
|
requireNonNull(objectName.getKeyPropertyList().get("scope")));
|
|
}
|
|
|
|
private Object[] makeTimerRow(ObjectName objectName)
|
|
{
|
|
assertEquals(MetricType.TIMER.metricName, objectName.getKeyPropertyList().get("name"));
|
|
CassandraMetricsRegistry.JmxTimerMBean bean = JMX.newMBeanProxy(jmxConnection,
|
|
objectName,
|
|
CassandraMetricsRegistry.JmxTimerMBean.class);
|
|
|
|
return CQLTester.row(getFullMetricName(objectName),
|
|
bean.getCount(),
|
|
bean.getMax(),
|
|
bean.getMean(),
|
|
bean.getMin(),
|
|
bean.get75thPercentile(),
|
|
bean.get95thPercentile(),
|
|
bean.get98thPercentile(),
|
|
bean.get999thPercentile(),
|
|
bean.get99thPercentile(),
|
|
bean.getFifteenMinuteRate(),
|
|
bean.getFiveMinuteRate(),
|
|
bean.getMeanRate(),
|
|
bean.getOneMinuteRate(),
|
|
requireNonNull(objectName.getKeyPropertyList().get("scope")));
|
|
}
|
|
|
|
private Object[] makeGaugeRow(ObjectName objectName)
|
|
{
|
|
assertEquals(MetricType.GAUGE.metricName, objectName.getKeyPropertyList().get("name"));
|
|
String jmxValue = String.valueOf(JMX.newMBeanProxy(jmxConnection,
|
|
objectName,
|
|
CassandraMetricsRegistry.JmxGaugeMBean.class).getValue());
|
|
|
|
return CQLTester.row(getFullMetricName(objectName),
|
|
requireNonNull(objectName.getKeyPropertyList().get("scope")),
|
|
jmxValue);
|
|
}
|
|
|
|
private boolean isLocalMetric(ObjectName mBean)
|
|
{
|
|
MetricType type = MetricType.find(mBean.getKeyPropertyList().get("name")).orElse(null);
|
|
return mBean.toString().startsWith(DefaultNameFactory.GROUP_NAME) && metricToNameMap.containsKey(type);
|
|
}
|
|
|
|
private static String getFullMetricName(ObjectName objectName)
|
|
{
|
|
Map<String, String> props = objectName.getKeyPropertyList();
|
|
return new CassandraMetricsRegistry.MetricName(DefaultNameFactory.GROUP_NAME, requireNonNull(props.get("type")),
|
|
requireNonNull(props.get("name")),
|
|
requireNonNull(props.get("scope"))).getMetricName();
|
|
}
|
|
|
|
private enum MetricType
|
|
{
|
|
METER("MeterTestMetric"),
|
|
COUNTER("CounterTestMetric"),
|
|
HISTOGRAM("HistogramTestMetric"),
|
|
TIMER("TimerTestMetric"),
|
|
GAUGE("GaugeTestMetric");
|
|
|
|
private final String metricName;
|
|
|
|
MetricType(String metricName)
|
|
{
|
|
this.metricName = metricName;
|
|
}
|
|
|
|
public static Optional<MetricType> find(String metricName)
|
|
{
|
|
for (MetricType type : values())
|
|
if (type.metricName.equals(metricName))
|
|
return Optional.of(type);
|
|
|
|
return Optional.empty();
|
|
}
|
|
}
|
|
}
|