diff --git a/README.md b/README.md index db973dd..4efbca0 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Default query execution time threshold is 25 milliseconds. #### Request Rate Module -Request Rate Module uses codahale metrics library to create rate measurement of executed queries. Rates are reported for select and upsert statements using configured reporters in configured periods. +Request Rate Module uses codahale metrics library to create rate measurement of executed queries. Rates are reported for configurable statement types and consistency levels using configured reporters in configured periods. Default reporting interval is 1 second. #### Metrics Module diff --git a/cassandra-diagnostics-commons/pom.xml b/cassandra-diagnostics-commons/pom.xml index 744e976..5938a56 100644 --- a/cassandra-diagnostics-commons/pom.xml +++ b/cassandra-diagnostics-commons/pom.xml @@ -4,7 +4,7 @@ io.smartcat cassandra-diagnostics - 1.4.3 + 1.4.4 cassandra-diagnostics-commons jar diff --git a/cassandra-diagnostics-commons/src/main/java/io/smartcat/cassandra/diagnostics/Query.java b/cassandra-diagnostics-commons/src/main/java/io/smartcat/cassandra/diagnostics/Query.java index 4d21571..f9b76f2 100644 --- a/cassandra-diagnostics-commons/src/main/java/io/smartcat/cassandra/diagnostics/Query.java +++ b/cassandra-diagnostics-commons/src/main/java/io/smartcat/cassandra/diagnostics/Query.java @@ -25,6 +25,71 @@ public enum StatementType { UNKNOWN } + /** + * Defines possible consistency levels. + */ + public enum ConsistencyLevel { + /** + * ANY consistency. + */ + ANY, + + /** + * ONE consistency. + */ + ONE, + + /** + * TWO consistency. + */ + TWO, + + /** + * THREE consistency. + */ + THREE, + + /** + * QUORUM consistency. + */ + QUORUM, + + /** + * ALL consistency. + */ + ALL, + + /** + * LOCAL_QUORUM consistency. + */ + LOCAL_QUORUM, + + /** + * EACH_QUORUM consistency. + */ + EACH_QUORUM, + + /** + * SERIAL consistency. + */ + SERIAL, + + /** + * LOCAL_SERIAL consistency. + */ + LOCAL_SERIAL, + + /** + * LOCAL_ONE consistency. + */ + LOCAL_ONE, + + /** + * Consistency level is unknown. + */ + UNKNOWN + } + private long startTimeInMilliseconds; private long executionTimeInMilliseconds; private String clientAddress; @@ -32,6 +97,7 @@ public enum StatementType { private String keyspace; private String tableName; private String statement; + private ConsistencyLevel consistencyLevel; /** * Query's execution start time. @@ -105,9 +171,18 @@ public String statement() { return statement; } + /** + * Consistency level. + * + * @return consistencyLevel + */ + public ConsistencyLevel consistencyLevel() { + return consistencyLevel; + } + private Query(final long startTimeInMilliseconds, final long executionTimeInMilliseconds, final String clientAddress, final StatementType statementType, final String keyspace, - final String tableName, final String statement) { + final String tableName, final String statement, final ConsistencyLevel consistencyLevel) { this.startTimeInMilliseconds = startTimeInMilliseconds; this.executionTimeInMilliseconds = executionTimeInMilliseconds; this.clientAddress = clientAddress; @@ -115,6 +190,7 @@ private Query(final long startTimeInMilliseconds, final long executionTimeInMill this.keyspace = keyspace; this.tableName = tableName; this.statement = statement; + this.consistencyLevel = consistencyLevel; } /** @@ -127,20 +203,22 @@ private Query(final long startTimeInMilliseconds, final long executionTimeInMill * @param keyspace query's key space * @param tableName query's table name * @param statement query's CQL statement + * @param consistencyLevel query's consistencyLevel * @return a new Query instance */ public static Query create(final long startTimeInMilliseconds, final long executionTimeInMilliseconds, final String clientAddress, final StatementType statementType, final String keyspace, - final String tableName, final String statement) { + final String tableName, final String statement, final ConsistencyLevel consistencyLevel) { return new Query(startTimeInMilliseconds, executionTimeInMilliseconds, clientAddress, statementType, keyspace, - tableName, statement); + tableName, statement, consistencyLevel); } @Override public String toString() { return "Query [ " + "startTimeInMilliseconds=" + startTimeInMilliseconds + ", executionTimeInMilliseconds=" + executionTimeInMilliseconds + ", clientAddress=" + clientAddress + ", statementType=" + statementType - .name() + ", statement=" + statement + ", keyspace=" + keyspace + ", tableName=" + tableName + " ]"; + .name() + ", statement=" + statement + ", keyspace=" + keyspace + ", tableName=" + tableName + ", " + + "consistencyLevel=" + consistencyLevel.name() + " ]"; } } diff --git a/cassandra-diagnostics-connector21/pom.xml b/cassandra-diagnostics-connector21/pom.xml index 4a66c6c..84a558a 100644 --- a/cassandra-diagnostics-connector21/pom.xml +++ b/cassandra-diagnostics-connector21/pom.xml @@ -4,7 +4,7 @@ io.smartcat cassandra-diagnostics - 1.4.3 + 1.4.4 cassandra-diagnostics-connector21 Cassandra Diagnostics Connector for Cassandra 2.1.x. diff --git a/cassandra-diagnostics-connector21/src/main/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapper.java b/cassandra-diagnostics-connector21/src/main/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapper.java index 86eb959..7fa8d5b 100644 --- a/cassandra-diagnostics-connector21/src/main/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapper.java +++ b/cassandra-diagnostics-connector21/src/main/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapper.java @@ -18,6 +18,7 @@ import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import io.smartcat.cassandra.diagnostics.Query; +import io.smartcat.cassandra.diagnostics.Query.ConsistencyLevel; /** * This class is a Diagnostics wrapper for {@link org.apache.cassandra.cql3.QueryProcessor}. @@ -172,18 +173,33 @@ private Query createQuery(final long startTime, final long execTime, final Strin private Query createQuery(final long startTime, final long execTime, final String queryString, final SelectStatement statement, final QueryState queryState, final QueryOptions options) { return Query.create(startTime, execTime, queryState.getClientState().getRemoteAddress().toString(), - Query.StatementType.SELECT, statement.keyspace(), statement.columnFamily(), queryString); + Query.StatementType.SELECT, statement.keyspace(), statement.columnFamily(), queryString, + extractConsistencyLevel(options)); } private Query createQuery(final long startTime, final long execTime, final String queryString, final ModificationStatement statement, final QueryState queryState, final QueryOptions options) { return Query.create(startTime, execTime, queryState.getClientState().getRemoteAddress().toString(), - Query.StatementType.UPDATE, statement.keyspace(), statement.columnFamily(), queryString); + Query.StatementType.UPDATE, statement.keyspace(), statement.columnFamily(), queryString, + extractConsistencyLevel(options)); } private Query createGenericQuery(final long startTime, final long execTime, final String queryString, final CQLStatement statement, final QueryState queryState, final QueryOptions options) { return Query.create(startTime, execTime, queryState.getClientState().getRemoteAddress().toString(), - Query.StatementType.UNKNOWN, "", "", queryString); + Query.StatementType.UNKNOWN, "", "", queryString, extractConsistencyLevel(options)); + } + + private ConsistencyLevel extractConsistencyLevel(final QueryOptions queryOptions) { + ConsistencyLevel queryConsistencyLevel = ConsistencyLevel.UNKNOWN; + + for (ConsistencyLevel consistencyLevel : ConsistencyLevel.values()) { + if (consistencyLevel.name().equals(queryOptions.getConsistency().name())) { + queryConsistencyLevel = consistencyLevel; + break; + } + } + + return queryConsistencyLevel; } } diff --git a/cassandra-diagnostics-connector21/src/test/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapperTest.java b/cassandra-diagnostics-connector21/src/test/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapperTest.java index 96cbd4a..f7f0095 100644 --- a/cassandra-diagnostics-connector21/src/test/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapperTest.java +++ b/cassandra-diagnostics-connector21/src/test/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapperTest.java @@ -14,6 +14,7 @@ import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.statements.SelectStatement; +import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.service.ClientState; import org.apache.cassandra.service.QueryState; import org.junit.Test; @@ -57,6 +58,7 @@ public void report(Query query) { when(queryState.getClientState()).thenReturn(clientState); QueryOptions options = mock(QueryOptions.class); + when(options.getConsistency()).thenReturn(ConsistencyLevel.ONE); wrapper.processPrepared(statement, queryState, options, System.currentTimeMillis(), null, null); @@ -66,6 +68,7 @@ public void report(Query query) { assertThat(reportedQuery.statementType()).isEqualTo(Query.StatementType.SELECT); assertThat(reportedQuery.keyspace()).isEqualTo("test_keyspace"); assertThat(reportedQuery.tableName()).isEqualTo("test_table"); + assertThat(reportedQuery.consistencyLevel()).isEqualTo(Query.ConsistencyLevel.ONE); } @Test diff --git a/cassandra-diagnostics-connector30/pom.xml b/cassandra-diagnostics-connector30/pom.xml index 378935b..c507920 100644 --- a/cassandra-diagnostics-connector30/pom.xml +++ b/cassandra-diagnostics-connector30/pom.xml @@ -5,7 +5,7 @@ io.smartcat cassandra-diagnostics - 1.4.3 + 1.4.4 cassandra-diagnostics-connector30 diff --git a/cassandra-diagnostics-connector30/src/main/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapper.java b/cassandra-diagnostics-connector30/src/main/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapper.java index 86eb959..7fa8d5b 100644 --- a/cassandra-diagnostics-connector30/src/main/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapper.java +++ b/cassandra-diagnostics-connector30/src/main/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapper.java @@ -18,6 +18,7 @@ import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import io.smartcat.cassandra.diagnostics.Query; +import io.smartcat.cassandra.diagnostics.Query.ConsistencyLevel; /** * This class is a Diagnostics wrapper for {@link org.apache.cassandra.cql3.QueryProcessor}. @@ -172,18 +173,33 @@ private Query createQuery(final long startTime, final long execTime, final Strin private Query createQuery(final long startTime, final long execTime, final String queryString, final SelectStatement statement, final QueryState queryState, final QueryOptions options) { return Query.create(startTime, execTime, queryState.getClientState().getRemoteAddress().toString(), - Query.StatementType.SELECT, statement.keyspace(), statement.columnFamily(), queryString); + Query.StatementType.SELECT, statement.keyspace(), statement.columnFamily(), queryString, + extractConsistencyLevel(options)); } private Query createQuery(final long startTime, final long execTime, final String queryString, final ModificationStatement statement, final QueryState queryState, final QueryOptions options) { return Query.create(startTime, execTime, queryState.getClientState().getRemoteAddress().toString(), - Query.StatementType.UPDATE, statement.keyspace(), statement.columnFamily(), queryString); + Query.StatementType.UPDATE, statement.keyspace(), statement.columnFamily(), queryString, + extractConsistencyLevel(options)); } private Query createGenericQuery(final long startTime, final long execTime, final String queryString, final CQLStatement statement, final QueryState queryState, final QueryOptions options) { return Query.create(startTime, execTime, queryState.getClientState().getRemoteAddress().toString(), - Query.StatementType.UNKNOWN, "", "", queryString); + Query.StatementType.UNKNOWN, "", "", queryString, extractConsistencyLevel(options)); + } + + private ConsistencyLevel extractConsistencyLevel(final QueryOptions queryOptions) { + ConsistencyLevel queryConsistencyLevel = ConsistencyLevel.UNKNOWN; + + for (ConsistencyLevel consistencyLevel : ConsistencyLevel.values()) { + if (consistencyLevel.name().equals(queryOptions.getConsistency().name())) { + queryConsistencyLevel = consistencyLevel; + break; + } + } + + return queryConsistencyLevel; } } diff --git a/cassandra-diagnostics-connector30/src/test/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapperTest.java b/cassandra-diagnostics-connector30/src/test/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapperTest.java index 96cbd4a..f7f0095 100644 --- a/cassandra-diagnostics-connector30/src/test/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapperTest.java +++ b/cassandra-diagnostics-connector30/src/test/java/io/smartcat/cassandra/diagnostics/connector/QueryProcessorWrapperTest.java @@ -14,6 +14,7 @@ import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.statements.SelectStatement; +import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.service.ClientState; import org.apache.cassandra.service.QueryState; import org.junit.Test; @@ -57,6 +58,7 @@ public void report(Query query) { when(queryState.getClientState()).thenReturn(clientState); QueryOptions options = mock(QueryOptions.class); + when(options.getConsistency()).thenReturn(ConsistencyLevel.ONE); wrapper.processPrepared(statement, queryState, options, System.currentTimeMillis(), null, null); @@ -66,6 +68,7 @@ public void report(Query query) { assertThat(reportedQuery.statementType()).isEqualTo(Query.StatementType.SELECT); assertThat(reportedQuery.keyspace()).isEqualTo("test_keyspace"); assertThat(reportedQuery.tableName()).isEqualTo("test_table"); + assertThat(reportedQuery.consistencyLevel()).isEqualTo(Query.ConsistencyLevel.ONE); } @Test diff --git a/cassandra-diagnostics-core/COREMODULES.md b/cassandra-diagnostics-core/COREMODULES.md index 40fd84a..5c2ec6b 100644 --- a/cassandra-diagnostics-core/COREMODULES.md +++ b/cassandra-diagnostics-core/COREMODULES.md @@ -58,7 +58,7 @@ Request Rate Module counts request rate of executed queries. Rates are reported #### Configuration -Measurement name is by default `request_rate` with `_update` and `_select` suffixes for upsert and select statements respectively. For precise measurements use a default reporting period of 1 second. If higher reporting period is used reported value represents average requests/second for the reporting period. +Measurement name is by default `request_rate`. You can configure which requests to report with `requestsToReport` which is a list of statement type - consistency level pairs. By default if you do not specify anything `*:*` will be used which counts all requests ignoring separation per statement type and consistency levels. You can configure only specific statement type ignoring consistency level with `SELECT:*` or you can opt to track all statament types of specific consistency level with `*:ONE`. For precise measurements use a default reporting period of 1 second. If higher reporting period is used reported value represents average requests/second for the reporting period. ``` - module: io.smartcat.cassandra.diagnostics.module.requestrate.RequestRateModule @@ -66,6 +66,9 @@ Measurement name is by default `request_rate` with `_update` and `_select` suffi options: period: 1 #optional timeunit: SECONDS #optional + requestsToReport: # optional + - SELECT:ALL + - UPDATE:* reporters: - io.smartcat.cassandra.diagnostics.reporter.LogReporter ``` @@ -88,7 +91,8 @@ Minimal configuration requires specifying `metricsPatterns` with required metric jmxSslEnabled: false #optional jmxSslUsername: username #optional, set if ssl enabled jmxSslPassword: password #optional, set if ssl enabled - metricsPackageName: "org.apache.cassandra.metrics" #optional + metricsPackageNames: + - "org.apache.cassandra.metrics" #optional metricsSeparator: "_" # optional, metrics measurement name separator metricsPatterns: - "^org.apache.cassandra.metrics.Cache.+" diff --git a/cassandra-diagnostics-core/pom.xml b/cassandra-diagnostics-core/pom.xml index a30356f..ff53d9c 100644 --- a/cassandra-diagnostics-core/pom.xml +++ b/cassandra-diagnostics-core/pom.xml @@ -4,7 +4,7 @@ io.smartcat cassandra-diagnostics - 1.4.3 + 1.4.4 cassandra-diagnostics-core jar diff --git a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsCollector.java b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsCollector.java index b1c20da..b23ece0 100644 --- a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsCollector.java +++ b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsCollector.java @@ -42,6 +42,8 @@ public class MetricsCollector { private static final String DEFAULT_SOCKET_FACTORY = "com.sun.jndi.rmi.factory.socket"; + private final String service; + private final MetricsConfiguration config; private final GlobalConfiguration globalConfiguration; @@ -50,15 +52,18 @@ public class MetricsCollector { private MBeanServerConnection mbeanServerConn; - private Set mbeans; + private Set mbeans = new HashSet<>(); /** * Constructor. * - * @param config metrics configuration + * @param service service name for measurements + * @param config metrics configuration * @param globalConfiguration Global diagnostics configuration */ - public MetricsCollector(final MetricsConfiguration config, final GlobalConfiguration globalConfiguration) { + public MetricsCollector(final String service, final MetricsConfiguration config, + final GlobalConfiguration globalConfiguration) { + this.service = service; this.config = config; this.globalConfiguration = globalConfiguration; } @@ -98,8 +103,10 @@ public boolean connect() { jmxc = JMXConnectorFactory.connect(jmxUrl, env); mbeanServerConn = jmxc.getMBeanServerConnection(); - String queryName = String.format("%s:*", config.metricsPackageName()); - mbeans = filterMBeans(mbeanServerConn.queryMBeans(new ObjectName(queryName), null)); + for (String packageName : config.metricsPackageNames()) { + String queryName = String.format("%s:*", packageName); + mbeans.addAll(filterMBeans(packageName, mbeanServerConn.queryMBeans(new ObjectName(queryName), null))); + } return true; } catch (IOException e) { @@ -129,7 +136,8 @@ public List collectMeasurements() { if (value != null) { measurements.add(createMeasurement( - mbean.getMeasurementName() + config.metricsSeparator() + attribute.getName(), + service + config.metricsSeparator() + mbean.getMeasurementName() + + config.metricsSeparator() + attribute.getName(), Double.parseDouble(value.toString()))); } @@ -143,15 +151,15 @@ public List collectMeasurements() { return measurements; } - private Measurement createMeasurement(String service, double value) { - final Map tags = new HashMap<>(1); + private Measurement createMeasurement(final String service, final double value) { + final Map tags = new HashMap<>(2); tags.put("host", globalConfiguration.hostname); tags.put("systemName", globalConfiguration.systemName); return Measurement.create(service, value, System.currentTimeMillis(), TimeUnit.MILLISECONDS, tags, new HashMap()); } - private Set filterMBeans(final Set mbeanObjectInstances) + private Set filterMBeans(final String packageName, final Set mbeanObjectInstances) throws IntrospectionException, ReflectionException, InstanceNotFoundException, IOException { Set results = new HashSet(); List patterns = new ArrayList(); @@ -178,7 +186,7 @@ private Set filterMBeans(final Set mbeanObjectInst } } - MetricsMBean mbean = new MetricsMBean(config, objectInstance, filteredAttributes); + MetricsMBean mbean = new MetricsMBean(packageName, config, objectInstance, filteredAttributes); boolean matches = false; if (patterns.isEmpty()) { diff --git a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsConfiguration.java b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsConfiguration.java index 6fd8773..f52214d 100644 --- a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsConfiguration.java +++ b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsConfiguration.java @@ -1,6 +1,7 @@ package io.smartcat.cassandra.diagnostics.module.metrics; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -25,8 +26,8 @@ public static class Values { private static final boolean DEFAULT_JMX_SSL_ENABLED = false; private static final String DEFAULT_JMX_SSL_USERNAME = null; private static final String DEFAULT_JMX_SSL_PASSWORD = null; - private static final String DEFAULT_METRICS_PACKAGE_NAME = "org.apache.cassandra.metrics"; - private static final String DEFAULT_METRICS_SEPARATOR = "_"; + private static final List DEFAULT_METRICS_PACKAGE_NAMES = Arrays.asList("org.apache.cassandra.metrics"); + private static final String DEFAULT_METRICS_SEPARATOR = "."; private static final List DEFAULT_METRICS_PATTERNS = new ArrayList(); /** @@ -67,7 +68,7 @@ public static class Values { /** * Metrics package name. */ - public String metricsPackageName = DEFAULT_METRICS_PACKAGE_NAME; + public List metricsPackageNames = DEFAULT_METRICS_PACKAGE_NAMES; /** * Metrics measurement name separator. @@ -174,12 +175,12 @@ public String jmxSslPassword() { } /** - * Metrics package name. Defaults to org.apache.cassandra.metrics:*. + * Metrics package names. Defaults to org.apache.cassandra.metrics:*. * - * @return metrics package name + * @return metrics package names */ - public String metricsPackageName() { - return values.metricsPackageName; + public List metricsPackageNames() { + return values.metricsPackageNames; } /** diff --git a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsMBean.java b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsMBean.java index 0fd4c01..e60724e 100644 --- a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsMBean.java +++ b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsMBean.java @@ -24,14 +24,15 @@ public class MetricsMBean { /** * Constructor. * + * @param packageName metrics package name * @param config metrics configuration * @param mbean mbean object instance * @param mbeanAttributes mbean attributes */ - public MetricsMBean(final MetricsConfiguration config, final ObjectInstance mbean, + public MetricsMBean(final String packageName, final MetricsConfiguration config, final ObjectInstance mbean, final List mbeanAttributes) { - this.mbeanName = getMBeanName(config.metricsPackageName(), mbean); - this.measurementName = nameBuilder(config.metricsPackageName(), mbean, config.metricsSeparator()); + this.mbeanName = buildMBeanName(packageName, mbean); + this.measurementName = buildMeasurementName(packageName, mbean, config.metricsSeparator()); this.mbean = mbean; this.mbeanAttributes = mbeanAttributes; } @@ -72,25 +73,17 @@ public List getMBeanAttributes() { return mbeanAttributes; } - /** - * Get mbean measurement names based on the metrics package naming pattern. - *

- * Everything is named based on this pattern: - *

- * type=*, scope=*, name=* - * type=ThreadPools, path=*, scope=*, name=* - * type=ColumnFamily, keyspace=*, scope=*, name=* - * type=Keyspace, keyspace=*, name=* - * - * @param metricsPackageName metrics package name - * @param mbean MBean object instance - * @return mbean measurement name - */ - private String getMBeanName(final String metricsPackageName, final ObjectInstance mbean) { - return nameBuilder(metricsPackageName, mbean, METRICS_PACKAGE_SEPARATOR); + private String buildMBeanName(final String metricsPackageName, final ObjectInstance mbean) { + return nameBuilder(metricsPackageName, mbean, METRICS_PACKAGE_SEPARATOR, true); } - private String nameBuilder(final String metricsPackageName, final ObjectInstance mbean, final String separator) { + private String buildMeasurementName(final String metricsPackageName, final ObjectInstance mbean, + final String separator) { + return nameBuilder(metricsPackageName, mbean, separator, false); + } + + private String nameBuilder(final String metricsPackageName, final ObjectInstance mbean, final String separator, + final boolean isMBeanName) { final String type = mbean.getObjectName().getKeyProperty("type"); final String path = mbean.getObjectName().getKeyProperty("path"); final String keyspace = mbean.getObjectName().getKeyProperty("keyspace"); @@ -99,8 +92,10 @@ private String nameBuilder(final String metricsPackageName, final ObjectInstance final String packageName = metricsPackageName.replace(".", separator); StringBuilder nameBuilder = new StringBuilder(); - nameBuilder.append(packageName); - nameBuilder.append(separator); + if (isMBeanName) { + nameBuilder.append(packageName); + nameBuilder.append(separator); + } nameBuilder.append(type); if (path != null && !path.isEmpty()) { nameBuilder.append(separator); diff --git a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsModule.java b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsModule.java index d0afcdc..d6243d3 100644 --- a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsModule.java +++ b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsModule.java @@ -21,6 +21,8 @@ public class MetricsModule extends Module { private static final Logger logger = LoggerFactory.getLogger(MetricsModule.class); + private static final String DEFAULT_MEASUREMENT_NAME = "metrics"; + private static final String METRICS_THREAD_NAME = "metrics-timer"; private final MetricsConfiguration config; @@ -42,7 +44,8 @@ public MetricsModule(ModuleConfiguration configuration, List reporters super(configuration, reporters, globalConfiguration); config = MetricsConfiguration.create(configuration.options); - metricsCollector = new MetricsCollector(config, globalConfiguration); + metricsCollector = new MetricsCollector(configuration.getMeasurementOrDefault(DEFAULT_MEASUREMENT_NAME), config, + globalConfiguration); logger.info("Metrics module initialized with {} {} reporting period.", config.period(), config.timeunit().name()); diff --git a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateConfiguration.java b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateConfiguration.java index ac133ed..09bc7ca 100644 --- a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateConfiguration.java +++ b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateConfiguration.java @@ -1,10 +1,13 @@ package io.smartcat.cassandra.diagnostics.module.requestrate; +import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import org.yaml.snakeyaml.Yaml; +import io.smartcat.cassandra.diagnostics.Query; import io.smartcat.cassandra.diagnostics.config.ConfigurationException; /** @@ -12,12 +15,30 @@ */ public class RequestRateConfiguration { + /** + * Report all statement types. + */ + public static final String ALL_STATEMENT_TYPES = "*"; + + /** + * Report all consistency levels. + */ + public static final String ALL_CONSISTENCY_LEVELS = "*"; + + /** + * Since request to report is combination between statement type and consistency level delimiter is used to glue + * those values together. + */ + public static final String REQUEST_META_DELIMITER = ":"; + /** * A helper class for constructing immutable outer class. */ public static class Values { private static final int DEFAULT_PERIOD = 1; private static final String DEFAULT_TIMEUNIT = "SECONDS"; + private static final List DEFAULT_REQUESTS_TO_REPORT = Arrays + .asList(ALL_STATEMENT_TYPES + REQUEST_META_DELIMITER + ALL_CONSISTENCY_LEVELS); /** * Request rate reporting period. @@ -28,6 +49,11 @@ public static class Values { * Request rate reporting time unit. */ public TimeUnit timeunit = TimeUnit.valueOf(DEFAULT_TIMEUNIT); + + /** + * Combination of statement type and consistency level to report. + */ + public List requestsToReport = DEFAULT_REQUESTS_TO_REPORT; } private Values values = new Values(); @@ -47,10 +73,48 @@ public static RequestRateConfiguration create(Map options) throw RequestRateConfiguration conf = new RequestRateConfiguration(); Yaml yaml = new Yaml(); String str = yaml.dumpAsMap(options); - conf.values = yaml.loadAs(str, RequestRateConfiguration.Values.class); + + try { + conf.values = yaml.loadAs(str, RequestRateConfiguration.Values.class); + } catch (Exception e) { + throw new ConfigurationException("Unable to load configuration.", e); + } + + validateRequestsToReport(conf); + return conf; } + private static void validateRequestsToReport(RequestRateConfiguration conf) throws ConfigurationException { + for (String requestToReport : conf.requestsToReport()) { + String[] requestMeta = requestToReport.split(REQUEST_META_DELIMITER); + + if (requestMeta.length != 2) { + throw new ConfigurationException( + "Only two configuration parameters supported, statement type and consistency level."); + } + + String statementType = requestMeta[0]; + String consistencyLevel = requestMeta[1]; + + try { + if (!statementType.equals(ALL_STATEMENT_TYPES)) { + Query.StatementType.valueOf(statementType); + } + } catch (IllegalArgumentException ex) { + throw new ConfigurationException("Illegal statement type configured: " + statementType); + } + + try { + if (!consistencyLevel.equals(ALL_CONSISTENCY_LEVELS)) { + Query.ConsistencyLevel.valueOf(consistencyLevel); + } + } catch (IllegalArgumentException ex) { + throw new ConfigurationException("Illegal consistency level configured: " + consistencyLevel); + } + } + } + /** * Request rate reporting period. * @@ -78,4 +142,12 @@ public long reportingRateInMillis() { return timeunit().toMillis(period()); } + /** + * Type of requests to report (combination of consistency level and statement type). + * + * @return list of requests to report + */ + public List requestsToReport() { + return values.requestsToReport; + } } diff --git a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateModule.java b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateModule.java index 9fe749c..f2d07ed 100644 --- a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateModule.java +++ b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateModule.java @@ -1,8 +1,14 @@ package io.smartcat.cassandra.diagnostics.module.requestrate; +import static io.smartcat.cassandra.diagnostics.module.requestrate.RequestRateConfiguration.ALL_CONSISTENCY_LEVELS; +import static io.smartcat.cassandra.diagnostics.module.requestrate.RequestRateConfiguration.ALL_STATEMENT_TYPES; +import static io.smartcat.cassandra.diagnostics.module.requestrate.RequestRateConfiguration.REQUEST_META_DELIMITER; + import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; @@ -13,7 +19,6 @@ import io.smartcat.cassandra.diagnostics.GlobalConfiguration; import io.smartcat.cassandra.diagnostics.Measurement; import io.smartcat.cassandra.diagnostics.Query; -import io.smartcat.cassandra.diagnostics.Query.StatementType; import io.smartcat.cassandra.diagnostics.config.ConfigurationException; import io.smartcat.cassandra.diagnostics.module.AtomicCounter; import io.smartcat.cassandra.diagnostics.module.Module; @@ -32,7 +37,7 @@ public class RequestRateModule extends Module { private static final String REQUEST_RATE_THREAD_NAME = "request-rate-timer"; - private final Map requestRates; + private final Set requestRates; private final String service; @@ -47,14 +52,13 @@ public class RequestRateModule extends Module { /** * Constructor. * - * @param configuration Module configuration - * @param reporters Reporter list - * @param globalConfiguration Global diagnostics configuration + * @param configuration Module configuration + * @param reporters Reporter list + * @param globalConfiguration Global diagnostics configuration * @throws ConfigurationException in case the provided module configuration is not valid */ public RequestRateModule(ModuleConfiguration configuration, List reporters, - final GlobalConfiguration globalConfiguration) - throws ConfigurationException { + final GlobalConfiguration globalConfiguration) throws ConfigurationException { super(configuration, reporters, globalConfiguration); RequestRateConfiguration config = RequestRateConfiguration.create(configuration.options); @@ -62,20 +66,64 @@ public RequestRateModule(ModuleConfiguration configuration, List repor period = config.period(); timeunit = config.timeunit(); rateFactor = timeunit.toSeconds(period); + requestRates = initRequestRates(config); - logger.info("RequestRate module initialized with {} {} reporting period.", period, timeunit.name()); - requestRates = new HashMap<>(); - for (StatementType statementType : StatementType.values()) { - requestRates.put(statementType, new AtomicCounter()); - } + logger.info("RequestRate module initialized with {} {} reporting period and requests to report: {}.", period, + timeunit.name(), config.requestsToReport()); timer = new Timer(REQUEST_RATE_THREAD_NAME); timer.schedule(new RequestRateTask(), 0, config.reportingRateInMillis()); } + /** + * Request rate class. + */ + private class RequestRate { + public final String statementType; + public final String consistencyLevel; + public final AtomicCounter counter = new AtomicCounter(); + + /** + * Constructor. + * + * @param requestPattern Configured request rate report pattern + */ + public RequestRate(String requestPattern) { + String[] requestMeta = requestPattern.split(REQUEST_META_DELIMITER); + this.statementType = requestMeta[0]; + this.consistencyLevel = requestMeta[1]; + } + + public void increment() { + counter.increment(); + } + + public long sumThenReset() { + return counter.sumThenReset(); + } + } + @Override public void process(Query query) { - requestRates.get(query.statementType()).increment(); + final String statementType = query.statementType().name(); + final String consistencyLevel = query.consistencyLevel().name(); + + for (RequestRate requestRate : requestRates) { + if (statementMatches(statementType, requestRate) + && consistencyLevelMatches(consistencyLevel, requestRate)) { + requestRate.increment(); + } + } + } + + private boolean consistencyLevelMatches(final String consistencyLevel, RequestRate requestRate) { + return requestRate.consistencyLevel.equals(ALL_CONSISTENCY_LEVELS) + || requestRate.consistencyLevel.equals(consistencyLevel); + } + + private boolean statementMatches(final String statementType, RequestRate requestRate) { + return requestRate.statementType.equals(ALL_STATEMENT_TYPES) + || requestRate.statementType.equals(statementType); } @Override @@ -84,6 +132,16 @@ public void stop() { timer.cancel(); } + private Set initRequestRates(RequestRateConfiguration config) { + final Set requestRates = new HashSet<>(); + + for (String requestToReport : config.requestsToReport()) { + requestRates.add(new RequestRate(requestToReport)); + } + + return requestRates; + } + private double convertRate(double rate) { return rate / rateFactor; } @@ -94,21 +152,19 @@ private double convertRate(double rate) { private class RequestRateTask extends TimerTask { @Override public void run() { - for (StatementType statementType : requestRates.keySet()) { - double requestRate = convertRate(requestRates.get(statementType).sumThenReset()); - - logger.debug("{} request rate: {}/{}", statementType, requestRate, timeunit.name()); - - report(createMeasurement(service, statementType, requestRate)); + for (RequestRate requestRate : requestRates) { + double rate = convertRate(requestRate.sumThenReset()); + report(createMeasurement(service, requestRate.statementType, requestRate.consistencyLevel, rate)); } } } - private Measurement createMeasurement(String service, StatementType statementType, double rate) { + private Measurement createMeasurement(String service, String statementType, String consistencyLevel, double rate) { final Map tags = new HashMap<>(1); tags.put("host", globalConfiguration.hostname); tags.put("systemName", globalConfiguration.systemName); - tags.put("statementType", statementType.toString()); + tags.put("statementType", statementType); + tags.put("consistencyLevel", consistencyLevel); return Measurement.create(service, rate, System.currentTimeMillis(), TimeUnit.MILLISECONDS, tags, new HashMap()); } diff --git a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/slowquery/SlowQueryModule.java b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/slowquery/SlowQueryModule.java index 4b53ad7..808a99e 100644 --- a/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/slowquery/SlowQueryModule.java +++ b/cassandra-diagnostics-core/src/main/java/io/smartcat/cassandra/diagnostics/module/slowquery/SlowQueryModule.java @@ -104,6 +104,7 @@ public void process(Query query) { final Map fields = new HashMap<>(4); fields.put("client", query.clientAddress()); fields.put("statement", query.statement()); + fields.put("consistencyLevel", query.consistencyLevel().name()); final Measurement measurement = Measurement.create(service, (double) query.executionTimeInMilliseconds(), query.startTimeInMilliseconds(), TimeUnit.MILLISECONDS, tags, fields); diff --git a/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsConfigurationTest.java b/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsConfigurationTest.java index 207c037..77f2864 100644 --- a/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsConfigurationTest.java +++ b/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsConfigurationTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -20,7 +21,7 @@ public void loads_default_configuration() throws Exception { assertThat(conf.jmxHost()).isEqualTo("127.0.0.1"); assertThat(conf.jmxPort()).isEqualTo(7199); assertThat(conf.jmxSslEnabled()).isEqualTo(false); - assertThat(conf.metricsPackageName()).isEqualTo("org.apache.cassandra.metrics"); + assertThat(conf.metricsPackageNames()).isEqualTo(Arrays.asList("org.apache.cassandra.metrics")); assertThat(conf.metricsPatterns()).isNotNull(); assertThat(conf.metricsPatterns()).isEmpty(); } diff --git a/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsModuleTest.java b/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsModuleTest.java index 4fe5527..84ad961 100644 --- a/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsModuleTest.java +++ b/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/metrics/MetricsModuleTest.java @@ -73,7 +73,7 @@ private ModuleConfiguration testConfiguration() { configuration.options.put("timeunit", "SECONDS"); configuration.options.put("jmxHost", "127.0.0.1"); configuration.options.put("jmxPort", 7199); - configuration.options.put("metricsPackageName", "io.smartcat.cassandra.diagnostics.module"); + configuration.options.put("metricsPackageNames", Arrays.asList("io.smartcat.cassandra.diagnostics.module")); configuration.options .put("metricsPatterns", Arrays.asList("^io.smartcat.cassandra.diagnostics.module.TestMXBean+")); return configuration; diff --git a/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateConfigurationTest.java b/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateConfigurationTest.java index 5005d0f..c8cfa61 100644 --- a/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateConfigurationTest.java +++ b/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateConfigurationTest.java @@ -2,12 +2,14 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import org.junit.Test; -import org.yaml.snakeyaml.constructor.ConstructorException; + +import io.smartcat.cassandra.diagnostics.config.ConfigurationException; public class RequestRateConfigurationTest { @@ -17,6 +19,7 @@ public void loads_default_configuration() throws Exception { RequestRateConfiguration conf = RequestRateConfiguration.create(options); assertThat(conf.period()).isEqualTo(1); assertThat(conf.timeunit()).isEqualTo(TimeUnit.SECONDS); + assertThat(conf.requestsToReport()).hasSize(1).contains("*:*"); } @Test @@ -24,9 +27,11 @@ public void provides_all_values() throws Exception { Map options = new HashMap<>(); options.put("period", 2); options.put("timeunit", "SECONDS"); + options.put("requestsToReport", Arrays.asList("SELECT:ALL")); RequestRateConfiguration conf = RequestRateConfiguration.create(options); assertThat(conf.period()).isEqualTo(2); assertThat(conf.timeunit()).isEqualTo(TimeUnit.SECONDS); + assertThat(conf.requestsToReport()).hasSize(1).contains("SELECT:ALL"); } @Test @@ -37,7 +42,43 @@ public void fails_when_incorrect_values_provided() { try { RequestRateConfiguration.create(options); } catch (Exception e) { - assertThat(e).isInstanceOf(ConstructorException.class); + assertThat(e).isInstanceOf(ConfigurationException.class); + } + } + + @Test + public void fails_when_incorrect_requests_to_report() { + Map options = new HashMap<>(); + options.put("requestsToReport", Arrays.asList("SOMETHING")); + try { + RequestRateConfiguration.create(options); + } catch (Exception e) { + assertThat(e).isInstanceOf(ConfigurationException.class) + .hasMessage("Only two configuration parameters supported, statement type and consistency level."); + } + } + + @Test + public void fails_when_incorrect_statement_type() { + Map options = new HashMap<>(); + options.put("requestsToReport", Arrays.asList("SOMETHING:LOCAL_ONE")); + try { + RequestRateConfiguration.create(options); + } catch (Exception e) { + assertThat(e).isInstanceOf(ConfigurationException.class) + .hasMessage("Illegal statement type configured: SOMETHING"); + } + } + + @Test + public void fails_when_incorrect_consistency_level() { + Map options = new HashMap<>(); + options.put("requestsToReport", Arrays.asList("SELECT:SOMETHING")); + try { + RequestRateConfiguration.create(options); + } catch (Exception e) { + assertThat(e).isInstanceOf(ConfigurationException.class) + .hasMessage("Illegal consistency level configured: SOMETHING"); } } diff --git a/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateModuleTest.java b/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateModuleTest.java index 857c378..49c883c 100644 --- a/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateModuleTest.java +++ b/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/requestrate/RequestRateModuleTest.java @@ -5,6 +5,7 @@ import static org.mockito.Mockito.when; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -25,14 +26,14 @@ public class RequestRateModuleTest { @Test public void should_load_default_configuration_and_initialize() throws ConfigurationException { - final RequestRateModule module = new RequestRateModule(testConfiguration(1), testReporters(), - GlobalConfiguration.getDefault()); + final RequestRateModule module = new RequestRateModule(testConfiguration(1, Arrays.asList("*:*")), + testReporters(), GlobalConfiguration.getDefault()); module.stop(); } @Test public void should_report_request_rate_when_started() throws ConfigurationException, InterruptedException { - final CountDownLatch latch = new CountDownLatch(2); + final CountDownLatch latch = new CountDownLatch(1); final LatchTestReporter testReporter = new LatchTestReporter(null, GlobalConfiguration.getDefault(), latch); final List reporters = new ArrayList() { { @@ -40,7 +41,7 @@ public void should_report_request_rate_when_started() throws ConfigurationExcept } }; - final RequestRateModule module = new RequestRateModule(testConfiguration(1), reporters, + final RequestRateModule module = new RequestRateModule(testConfiguration(1, Arrays.asList("*:*")), reporters, GlobalConfiguration.getDefault()); boolean wait = latch.await(100, TimeUnit.MILLISECONDS); module.stop(); @@ -60,10 +61,13 @@ public void should_report_exact_request_rate_values() throws ConfigurationExcept final Query selectQuery = mock(Query.class); when(selectQuery.statementType()).thenReturn(Query.StatementType.SELECT); + when(selectQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); final Query updateQuery = mock(Query.class); when(updateQuery.statementType()).thenReturn(Query.StatementType.UPDATE); + when(updateQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); - final RequestRateModule module = new RequestRateModule(testConfiguration(1), reporters, + final RequestRateModule module = new RequestRateModule( + testConfiguration(1, Arrays.asList("SELECT:ALL", "UPDATE:ALL")), reporters, GlobalConfiguration.getDefault()); final long numberOfRequests = 1000; @@ -91,6 +95,179 @@ public void should_report_exact_request_rate_values() throws ConfigurationExcept assertThat(totalRequests).isEqualTo(numberOfRequests); } + @Test + public void should_report_request_rates_for_only_configured_statements_and_consistency_values() + throws ConfigurationException, InterruptedException { + final CountDownLatch latch = new CountDownLatch(20); + final LatchTestReporter latchTestReporter = new LatchTestReporter(null, GlobalConfiguration.getDefault(), + latch); + final List reporters = new ArrayList() { + { + add(latchTestReporter); + } + }; + + final Query selectQuery = mock(Query.class); + when(selectQuery.statementType()).thenReturn(Query.StatementType.SELECT); + when(selectQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); + final Query updateQuery = mock(Query.class); + when(updateQuery.statementType()).thenReturn(Query.StatementType.UPDATE); + when(updateQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); + final Query updateQueryWithLowerConsistency = mock(Query.class); + when(updateQueryWithLowerConsistency.statementType()).thenReturn(Query.StatementType.UPDATE); + when(updateQueryWithLowerConsistency.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ONE); + final Query unknownQuery = mock(Query.class); + when(unknownQuery.statementType()).thenReturn(Query.StatementType.UNKNOWN); + when(unknownQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); + + final RequestRateModule module = new RequestRateModule( + testConfiguration(1, Arrays.asList("SELECT:ALL", "UPDATE:ALL")), reporters, + GlobalConfiguration.getDefault()); + + final long numberOfRequests = 1000; + // only half of requests will be reported, unknown requests and lower consistency requests will be ignored + final long expectedNumberOfRequests = 500; + for (int i = 0; i < numberOfRequests / 4; i++) { + module.process(selectQuery); + module.process(updateQuery); + module.process(updateQueryWithLowerConsistency); + module.process(unknownQuery); + } + + long requestRate = 0; + while (requestRate < expectedNumberOfRequests) { + requestRate = 0; + for (final Measurement measurement : latchTestReporter.getReported()) { + requestRate += measurement.getValue(); + } + latch.await(1100, TimeUnit.MILLISECONDS); + } + + module.stop(); + + long totalRequests = 0; + for (final Measurement measurement : latchTestReporter.getReported()) { + totalRequests += measurement.getValue(); + } + + assertThat(totalRequests).isEqualTo(expectedNumberOfRequests); + } + + @Test + public void should_report_request_rates_for_only_selects_and_all_consistency_values() + throws ConfigurationException, InterruptedException { + final CountDownLatch latch = new CountDownLatch(20); + final LatchTestReporter latchTestReporter = new LatchTestReporter(null, GlobalConfiguration.getDefault(), + latch); + final List reporters = new ArrayList() { + { + add(latchTestReporter); + } + }; + + final Query selectQuery = mock(Query.class); + when(selectQuery.statementType()).thenReturn(Query.StatementType.SELECT); + when(selectQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); + final Query updateQuery = mock(Query.class); + when(updateQuery.statementType()).thenReturn(Query.StatementType.UPDATE); + when(updateQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); + final Query updateQueryWithLowerConsistency = mock(Query.class); + when(updateQueryWithLowerConsistency.statementType()).thenReturn(Query.StatementType.SELECT); + when(updateQueryWithLowerConsistency.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ONE); + final Query unknownQuery = mock(Query.class); + when(unknownQuery.statementType()).thenReturn(Query.StatementType.UNKNOWN); + when(unknownQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); + + final RequestRateModule module = new RequestRateModule( + testConfiguration(1, Arrays.asList("SELECT:*")), reporters, + GlobalConfiguration.getDefault()); + + final long numberOfRequests = 1000; + // only half of requests will be reported, unknown and update requests will be ignored + final long expectedNumberOfRequests = 500; + for (int i = 0; i < numberOfRequests / 4; i++) { + module.process(selectQuery); + module.process(updateQuery); + module.process(updateQueryWithLowerConsistency); + module.process(unknownQuery); + } + + long requestRate = 0; + while (requestRate < expectedNumberOfRequests) { + requestRate = 0; + for (final Measurement measurement : latchTestReporter.getReported()) { + requestRate += measurement.getValue(); + } + latch.await(1100, TimeUnit.MILLISECONDS); + } + + module.stop(); + + long totalRequests = 0; + for (final Measurement measurement : latchTestReporter.getReported()) { + totalRequests += measurement.getValue(); + } + + assertThat(totalRequests).isEqualTo(expectedNumberOfRequests); + } + + @Test + public void should_report_request_rates_for_all_statement_types_and_only_all_consistency_value() + throws ConfigurationException, InterruptedException { + final CountDownLatch latch = new CountDownLatch(20); + final LatchTestReporter latchTestReporter = new LatchTestReporter(null, GlobalConfiguration.getDefault(), + latch); + final List reporters = new ArrayList() { + { + add(latchTestReporter); + } + }; + + final Query selectQuery = mock(Query.class); + when(selectQuery.statementType()).thenReturn(Query.StatementType.SELECT); + when(selectQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); + final Query updateQuery = mock(Query.class); + when(updateQuery.statementType()).thenReturn(Query.StatementType.UPDATE); + when(updateQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); + final Query updateQueryWithLowerConsistency = mock(Query.class); + when(updateQueryWithLowerConsistency.statementType()).thenReturn(Query.StatementType.SELECT); + when(updateQueryWithLowerConsistency.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ONE); + final Query unknownQuery = mock(Query.class); + when(unknownQuery.statementType()).thenReturn(Query.StatementType.UNKNOWN); + when(unknownQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); + + final RequestRateModule module = new RequestRateModule(testConfiguration(1, Arrays.asList("*:ALL")), + reporters, GlobalConfiguration.getDefault()); + + final long numberOfRequests = 1000; + // only 2/3 of requests will be reported, lower consistency requests will be ignored + final long expectedNumberOfRequests = 750; + for (int i = 0; i < numberOfRequests / 4; i++) { + module.process(selectQuery); + module.process(updateQuery); + module.process(updateQueryWithLowerConsistency); + module.process(unknownQuery); + } + + long requestRate = 0; + while (requestRate < expectedNumberOfRequests) { + requestRate = 0; + for (final Measurement measurement : latchTestReporter.getReported()) { + requestRate += measurement.getValue(); + } + latch.await(1100, TimeUnit.MILLISECONDS); + } + + module.stop(); + + long totalRequests = 0; + for (final Measurement measurement : latchTestReporter.getReported()) { + totalRequests += measurement.getValue(); + } + + assertThat(totalRequests).isEqualTo(expectedNumberOfRequests); + } + @Test public void should_report_using_log_reporter() throws ConfigurationException, InterruptedException { final CountDownLatch latch = new CountDownLatch(1); @@ -103,7 +280,8 @@ public void should_report_using_log_reporter() throws ConfigurationException, In } }; - final RequestRateModule module = new RequestRateModule(testConfiguration(1), reporters, + final RequestRateModule module = new RequestRateModule( + testConfiguration(1, Arrays.asList("*:*")), reporters, GlobalConfiguration.getDefault()); boolean wait = latch.await(200, TimeUnit.MILLISECONDS); module.stop(); @@ -123,8 +301,10 @@ public void should_report_average_request_rate_for_period() throws Configuration final Query selectQuery = mock(Query.class); when(selectQuery.statementType()).thenReturn(Query.StatementType.SELECT); + when(selectQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); - final RequestRateModule module = new RequestRateModule(testConfiguration(2), reporters, + final RequestRateModule module = new RequestRateModule( + testConfiguration(2, Arrays.asList("*:*")), reporters, GlobalConfiguration.getDefault()); final long numberOfRequests = 1000; @@ -151,12 +331,13 @@ public void should_report_average_request_rate_for_period() throws Configuration assertThat(totalRequests).isEqualTo(numberOfRequests / 2); } - private ModuleConfiguration testConfiguration(final int period) { + private ModuleConfiguration testConfiguration(final int period, final List requestsToReport) { final ModuleConfiguration configuration = new ModuleConfiguration(); configuration.measurement = "test_measurement"; configuration.module = "io.smartcat.cassandra.diagnostics.module.requestrate.RequestRateModule"; configuration.options.put("period", period); configuration.options.put("timeunit", "SECONDS"); + configuration.options.put("requestsToReport", requestsToReport); return configuration; } diff --git a/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/slowquery/SlowQueryLogDeciderTest.java b/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/slowquery/SlowQueryLogDeciderTest.java index 094417d..90bea5f 100644 --- a/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/slowquery/SlowQueryLogDeciderTest.java +++ b/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/slowquery/SlowQueryLogDeciderTest.java @@ -9,6 +9,7 @@ import org.junit.Test; import io.smartcat.cassandra.diagnostics.Query; +import io.smartcat.cassandra.diagnostics.Query.ConsistencyLevel; import io.smartcat.cassandra.diagnostics.Query.StatementType; /** @@ -109,7 +110,8 @@ public void do_not_log_query_when_statement_is_not_for_logged_table() throws Exc } private Query buildQuery(long executionTime, StatementType type, String keyspace, String table) { - return Query.create(1, executionTime, "clientAddress", type, keyspace, table, "statement"); + return Query.create(1, executionTime, "clientAddress", type, keyspace, table, "statement", + ConsistencyLevel.ONE); } private SlowQueryLogDecider buildSlowLogDecider(final Map options) throws Exception { diff --git a/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/slowquery/SlowQueryModuleTest.java b/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/slowquery/SlowQueryModuleTest.java index 825d4c2..756f243 100644 --- a/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/slowquery/SlowQueryModuleTest.java +++ b/cassandra-diagnostics-core/src/test/java/io/smartcat/cassandra/diagnostics/module/slowquery/SlowQueryModuleTest.java @@ -16,6 +16,7 @@ import io.smartcat.cassandra.diagnostics.GlobalConfiguration; import io.smartcat.cassandra.diagnostics.Measurement; import io.smartcat.cassandra.diagnostics.Query; +import io.smartcat.cassandra.diagnostics.Query.ConsistencyLevel; import io.smartcat.cassandra.diagnostics.config.ConfigurationException; import io.smartcat.cassandra.diagnostics.module.LatchTestReporter; import io.smartcat.cassandra.diagnostics.module.ModuleConfiguration; @@ -37,16 +38,17 @@ public void should_transform() throws ConfigurationException { SlowQueryModule module = new SlowQueryModule(conf, testReporters(reporter), GlobalConfiguration.getDefault()); Query query = Query.create(1474741407205L, 234L, "/127.0.0.1:40042", Query.StatementType.SELECT, "keyspace", - "table", "select count(*) from keyspace.table"); + "table", "select count(*) from keyspace.table", ConsistencyLevel.ONE); module.process(query); module.stop(); Measurement measurement = reporter.reported.get(0); - assertThat(measurement.fields().keySet()).isEqualTo(Sets.newSet("statement", "client")); + assertThat(measurement.fields().keySet()).isEqualTo(Sets.newSet("statement", "client", "consistencyLevel")); assertThat(measurement.fields().get("statement")).isEqualTo("select count(*) from keyspace.table"); assertThat(measurement.fields().get("client")).isEqualTo("/127.0.0.1:40042"); + assertThat(measurement.fields().get("consistencyLevel")).isEqualTo("ONE"); assertThat(measurement.hasValue()).isTrue(); assertThat(measurement.getValue()).isEqualTo(234); @@ -67,8 +69,10 @@ public void should_report_number_of_slow_queries() throws ConfigurationException final Query selectQuery = mock(Query.class); when(selectQuery.statementType()).thenReturn(Query.StatementType.SELECT); + when(selectQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); final Query updateQuery = mock(Query.class); when(updateQuery.statementType()).thenReturn(Query.StatementType.UPDATE); + when(updateQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); final SlowQueryModule module = new SlowQueryModule(testConfiguration(1), reporters, GlobalConfiguration.getDefault()); @@ -123,8 +127,10 @@ public void should_not_report_any_slow_queries() throws InterruptedException, Co final Query selectQuery = mock(Query.class); when(selectQuery.statementType()).thenReturn(Query.StatementType.SELECT); + when(selectQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); final Query updateQuery = mock(Query.class); when(updateQuery.statementType()).thenReturn(Query.StatementType.UPDATE); + when(updateQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); final SlowQueryModule module = new SlowQueryModule(configuration, reporters, GlobalConfiguration.getDefault()); @@ -163,8 +169,10 @@ public void should_not_log_all_types_if_slow_query_logging_is_disabled() final Query selectQuery = mock(Query.class); when(selectQuery.statementType()).thenReturn(Query.StatementType.SELECT); + when(selectQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); final Query updateQuery = mock(Query.class); when(updateQuery.statementType()).thenReturn(Query.StatementType.UPDATE); + when(updateQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); final SlowQueryModule module = new SlowQueryModule(configuration, reporters, GlobalConfiguration.getDefault()); @@ -203,8 +211,10 @@ public void should_log_only_mutation_statements_when_only_mutation_type_logging_ final Query selectQuery = mock(Query.class); when(selectQuery.statementType()).thenReturn(Query.StatementType.SELECT); + when(selectQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); final Query updateQuery = mock(Query.class); when(updateQuery.statementType()).thenReturn(Query.StatementType.UPDATE); + when(updateQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); final SlowQueryModule module = new SlowQueryModule(configuration, reporters, GlobalConfiguration.getDefault()); @@ -246,8 +256,10 @@ public void should_log_all_statements_when_all_types_logging_is_enabled() final Query selectQuery = mock(Query.class); when(selectQuery.statementType()).thenReturn(Query.StatementType.SELECT); + when(selectQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); final Query updateQuery = mock(Query.class); when(updateQuery.statementType()).thenReturn(Query.StatementType.UPDATE); + when(updateQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); final SlowQueryModule module = new SlowQueryModule(configuration, reporters, GlobalConfiguration.getDefault()); @@ -297,8 +309,10 @@ public void should_log_mutation_and_select_statements_when_mutation_and_select_t final Query selectQuery = mock(Query.class); when(selectQuery.statementType()).thenReturn(Query.StatementType.SELECT); + when(selectQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); final Query updateQuery = mock(Query.class); when(updateQuery.statementType()).thenReturn(Query.StatementType.UPDATE); + when(updateQuery.consistencyLevel()).thenReturn(Query.ConsistencyLevel.ALL); final SlowQueryModule module = new SlowQueryModule(configuration, reporters, GlobalConfiguration.getDefault()); diff --git a/cassandra-diagnostics-driver-connector/pom.xml b/cassandra-diagnostics-driver-connector/pom.xml index 4d2c16e..f52d5af 100644 --- a/cassandra-diagnostics-driver-connector/pom.xml +++ b/cassandra-diagnostics-driver-connector/pom.xml @@ -5,7 +5,7 @@ io.smartcat cassandra-diagnostics - 1.4.3 + 1.4.4 cassandra-diagnostics-driver-connector diff --git a/cassandra-diagnostics-driver-connector/src/main/java/io/smartcat/cassandra/diagnostics/connector/ExecuteStatementWrapper.java b/cassandra-diagnostics-driver-connector/src/main/java/io/smartcat/cassandra/diagnostics/connector/ExecuteStatementWrapper.java index 89a023f..70104fd 100644 --- a/cassandra-diagnostics-driver-connector/src/main/java/io/smartcat/cassandra/diagnostics/connector/ExecuteStatementWrapper.java +++ b/cassandra-diagnostics-driver-connector/src/main/java/io/smartcat/cassandra/diagnostics/connector/ExecuteStatementWrapper.java @@ -11,6 +11,7 @@ import io.smartcat.cassandra.diagnostics.GlobalConfiguration; import io.smartcat.cassandra.diagnostics.Query; +import io.smartcat.cassandra.diagnostics.Query.ConsistencyLevel; /** * This class is a Diagnostics wrapper for driver session manager execute async method. @@ -76,7 +77,8 @@ public void run() { private Query extractQuery(final long startTime, final long execTime, final Statement statement) { final String queryString = statementQueryString(statement); final Query.StatementType queryType = queryType(queryString); - return Query.create(startTime, execTime, host, queryType, statement.getKeyspace(), "", queryString); + return Query.create(startTime, execTime, host, queryType, statement.getKeyspace(), "", queryString, + extractConsistencyLevel(statement)); } private String statementQueryString(final Statement statement) { @@ -124,4 +126,21 @@ private Query.StatementType queryType(final String query) { } return type; } + + private ConsistencyLevel extractConsistencyLevel(final Statement statement) { + if (statement.getConsistencyLevel() == null) { + return ConsistencyLevel.UNKNOWN; + } + + ConsistencyLevel queryConsistencyLevel = ConsistencyLevel.UNKNOWN; + + for (ConsistencyLevel consistencyLevel : ConsistencyLevel.values()) { + if (consistencyLevel.name().equals(statement.getConsistencyLevel().name())) { + queryConsistencyLevel = consistencyLevel; + break; + } + } + + return queryConsistencyLevel; + } } diff --git a/cassandra-diagnostics-driver-connector/src/test/java/io/smartcat/cassandra/diagnostics/connector/ExecuteStatementWrapperTest.java b/cassandra-diagnostics-driver-connector/src/test/java/io/smartcat/cassandra/diagnostics/connector/ExecuteStatementWrapperTest.java index b54d8d9..e8220a7 100644 --- a/cassandra-diagnostics-driver-connector/src/test/java/io/smartcat/cassandra/diagnostics/connector/ExecuteStatementWrapperTest.java +++ b/cassandra-diagnostics-driver-connector/src/test/java/io/smartcat/cassandra/diagnostics/connector/ExecuteStatementWrapperTest.java @@ -11,6 +11,7 @@ import org.junit.Test; import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.ConsistencyLevel; import com.datastax.driver.core.PreparedStatement; import com.datastax.driver.core.RegularStatement; import com.datastax.driver.core.ResultSetFuture; @@ -46,6 +47,7 @@ public void process_regular_select_statement() throws Exception { when(session.executeAsync(any(Statement.class))).thenReturn(result); when(statement.getKeyspace()).thenReturn("test_keyspace"); when(statement.getQueryString()).thenReturn("SELECT * FROM test_table WHERE id = 1"); + when(statement.getConsistencyLevel()).thenReturn(ConsistencyLevel.ALL); wrapper.processStatement(statement, System.currentTimeMillis(), result); @@ -54,6 +56,7 @@ public void process_regular_select_statement() throws Exception { assertThat(reporter.reportedQuery.keyspace()).isEqualTo("test_keyspace"); assertThat(reporter.reportedQuery.statement()).isEqualTo("SELECT * FROM test_table WHERE id = 1;"); assertThat(reporter.reportedQuery.statementType()).isEqualTo(Query.StatementType.SELECT); + assertThat(reporter.reportedQuery.consistencyLevel()).isEqualTo(Query.ConsistencyLevel.ALL); } @Test @@ -70,6 +73,7 @@ public void process_regular_update_statement() throws Exception { when(session.executeAsync(any(Statement.class))).thenReturn(result); when(statement.getKeyspace()).thenReturn("test_keyspace"); when(statement.getQueryString()).thenReturn("INSERT INTO test_table"); + when(statement.getConsistencyLevel()).thenReturn(ConsistencyLevel.ALL); wrapper.processStatement(statement, System.currentTimeMillis(), result); @@ -78,6 +82,7 @@ public void process_regular_update_statement() throws Exception { assertThat(reporter.reportedQuery.keyspace()).isEqualTo("test_keyspace"); assertThat(reporter.reportedQuery.statement()).isEqualTo("INSERT INTO test_table;"); assertThat(reporter.reportedQuery.statementType()).isEqualTo(Query.StatementType.UPDATE); + assertThat(reporter.reportedQuery.consistencyLevel()).isEqualTo(Query.ConsistencyLevel.ALL); } @Test @@ -96,6 +101,7 @@ public void process_bound_select_statement() throws Exception { when(statement.getKeyspace()).thenReturn("test_keyspace"); when(statement.preparedStatement()).thenReturn(pstm); when(pstm.getQueryString()).thenReturn("SELECT * FROM test_table WHERE id = 1"); + when(statement.getConsistencyLevel()).thenReturn(ConsistencyLevel.ALL); wrapper.processStatement(statement, System.currentTimeMillis(), result); @@ -104,7 +110,7 @@ public void process_bound_select_statement() throws Exception { assertThat(reporter.reportedQuery.keyspace()).isEqualTo("test_keyspace"); assertThat(reporter.reportedQuery.statement()).isEqualTo("SELECT * FROM test_table WHERE id = 1;"); assertThat(reporter.reportedQuery.statementType()).isEqualTo(Query.StatementType.SELECT); - System.out.println(reporter.reportedQuery); + assertThat(reporter.reportedQuery.consistencyLevel()).isEqualTo(Query.ConsistencyLevel.ALL); } } diff --git a/cassandra-diagnostics-embedded-cassandra/pom.xml b/cassandra-diagnostics-embedded-cassandra/pom.xml index 60028f8..0734c2c 100644 --- a/cassandra-diagnostics-embedded-cassandra/pom.xml +++ b/cassandra-diagnostics-embedded-cassandra/pom.xml @@ -4,7 +4,7 @@ io.smartcat cassandra-diagnostics - 1.4.3 + 1.4.4 cassandra-diagnostics-embedded-cassandra jar diff --git a/cassandra-diagnostics-ft/pom.xml b/cassandra-diagnostics-ft/pom.xml index 02aa87f..83ab170 100644 --- a/cassandra-diagnostics-ft/pom.xml +++ b/cassandra-diagnostics-ft/pom.xml @@ -4,7 +4,7 @@ io.smartcat cassandra-diagnostics - 1.4.3 + 1.4.4 cassandra-diagnostics-ft Cassandra Diagnostics functional test diff --git a/cassandra-diagnostics-reporter-datadog/pom.xml b/cassandra-diagnostics-reporter-datadog/pom.xml index 6d72984..0a01b94 100644 --- a/cassandra-diagnostics-reporter-datadog/pom.xml +++ b/cassandra-diagnostics-reporter-datadog/pom.xml @@ -4,7 +4,7 @@ cassandra-diagnostics io.smartcat - 1.4.3 + 1.4.4 cassandra-diagnostics-reporter-datadog diff --git a/cassandra-diagnostics-reporter-influx/pom.xml b/cassandra-diagnostics-reporter-influx/pom.xml index 8d9e149..df5c223 100644 --- a/cassandra-diagnostics-reporter-influx/pom.xml +++ b/cassandra-diagnostics-reporter-influx/pom.xml @@ -4,7 +4,7 @@ io.smartcat cassandra-diagnostics - 1.4.3 + 1.4.4 cassandra-diagnostics-reporter-influx diff --git a/cassandra-diagnostics-reporter-kafka/pom.xml b/cassandra-diagnostics-reporter-kafka/pom.xml index 13bf785..bf955c3 100644 --- a/cassandra-diagnostics-reporter-kafka/pom.xml +++ b/cassandra-diagnostics-reporter-kafka/pom.xml @@ -6,7 +6,7 @@ io.smartcat cassandra-diagnostics - 1.4.3 + 1.4.4 cassandra-diagnostics-reporter-kafka diff --git a/cassandra-diagnostics-reporter-riemann/pom.xml b/cassandra-diagnostics-reporter-riemann/pom.xml index 32c1ab2..752216a 100644 --- a/cassandra-diagnostics-reporter-riemann/pom.xml +++ b/cassandra-diagnostics-reporter-riemann/pom.xml @@ -4,7 +4,7 @@ io.smartcat cassandra-diagnostics - 1.4.3 + 1.4.4 cassandra-diagnostics-reporter-riemann diff --git a/cassandra-diagnostics-reporter-telegraf/pom.xml b/cassandra-diagnostics-reporter-telegraf/pom.xml index 036e03f..407b372 100644 --- a/cassandra-diagnostics-reporter-telegraf/pom.xml +++ b/cassandra-diagnostics-reporter-telegraf/pom.xml @@ -6,7 +6,7 @@ io.smartcat cassandra-diagnostics - 1.4.3 + 1.4.4 cassandra-diagnostics-reporter-telegraf diff --git a/pom.xml b/pom.xml index ae7833e..bfe5029 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.smartcat cassandra-diagnostics - 1.4.3 + 1.4.4 pom Cassandra Diagnostics