diff --git a/src/java/org/apache/cassandra/cql3/QueryEvents.java b/src/java/org/apache/cassandra/cql3/QueryEvents.java index 52cc352f5c27..599fc617ae9d 100644 --- a/src/java/org/apache/cassandra/cql3/QueryEvents.java +++ b/src/java/org/apache/cassandra/cql3/QueryEvents.java @@ -33,6 +33,13 @@ import org.apache.cassandra.cql3.statements.AuthenticationStatement; import org.apache.cassandra.cql3.statements.BatchStatement; +import org.apache.cassandra.exceptions.InvalidRequestException; +import org.apache.cassandra.exceptions.RequestFailureException; +import org.apache.cassandra.exceptions.RequestTimeoutException; +import org.apache.cassandra.exceptions.UnavailableException; +import org.apache.cassandra.exceptions.WriteTimeoutException; +import org.apache.cassandra.metrics.ClientRequestsMetrics; +import org.apache.cassandra.metrics.ClientRequestsMetricsProvider; import org.apache.cassandra.service.QueryState; import org.apache.cassandra.transport.Message; import org.apache.cassandra.transport.messages.ResultMessage; @@ -83,12 +90,31 @@ public void notifyQuerySuccess(CQLStatement statement, } } + private void updateMetrics(CQLStatement statement, Exception cause) + { + if (statement instanceof CQLStatement.SingleKeyspaceCqlStatement) + { + ClientRequestsMetrics metrics = ClientRequestsMetricsProvider.instance.metrics(((CQLStatement.SingleKeyspaceCqlStatement) statement).keyspace()); + if (cause instanceof InvalidRequestException) + metrics.allRequestsMetrics.invalid.mark(); + else if (cause instanceof UnavailableException) + metrics.allRequestsMetrics.unavailables.mark(); + else if (cause instanceof RequestTimeoutException) + metrics.allRequestsMetrics.timeouts.mark(); + else if (cause instanceof RequestFailureException) + metrics.allRequestsMetrics.failures.mark(); + else + metrics.allRequestsMetrics.otherErrors.mark(); + } + } + public void notifyQueryFailure(CQLStatement statement, String query, QueryOptions options, QueryState state, Exception cause) { + updateMetrics(statement, cause); try { final String maybeObfuscatedQuery = listeners.size() > 0 ? maybeObfuscatePassword(statement, query) : query; @@ -128,6 +154,9 @@ public void notifyExecuteFailure(QueryHandler.Prepared prepared, Exception cause) { CQLStatement statement = prepared != null ? prepared.statement : null; + + updateMetrics(statement, cause); + String query = prepared != null ? prepared.statement.getRawCQLStatement() : null; try { @@ -182,6 +211,10 @@ public void notifyBatchFailure(List prepared, queries.add(p.statement.getRawCQLStatement()); }); } + + if (!statements.isEmpty()) + updateMetrics(statements.get(0), cause); + try { for (Listener listener : listeners) diff --git a/src/java/org/apache/cassandra/metrics/AllRequestsMetrics.java b/src/java/org/apache/cassandra/metrics/AllRequestsMetrics.java new file mode 100644 index 000000000000..ae4ee7dfb7e9 --- /dev/null +++ b/src/java/org/apache/cassandra/metrics/AllRequestsMetrics.java @@ -0,0 +1,48 @@ +/* + * + * 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 com.codahale.metrics.Meter; + +import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics; + + +public class AllRequestsMetrics extends ClientRequestMetrics +{ + public final Meter invalid; + public final Meter otherErrors; + + public AllRequestsMetrics(String scope, String prefix) + { + super(scope, prefix); + invalid = Metrics.meter(factory.createMetricName(namePrefix + "Invalid")); + otherErrors = Metrics.meter(factory.createMetricName(namePrefix + "OtherErrors")); + } + + @Override + public void release() + { + super.release(); + Metrics.remove(factory.createMetricName(namePrefix + "Invalid")); + Metrics.remove(factory.createMetricName(namePrefix + "OtherErrors")); + } +} diff --git a/src/java/org/apache/cassandra/metrics/ClientRequestsMetrics.java b/src/java/org/apache/cassandra/metrics/ClientRequestsMetrics.java index 757384b61612..ed2473102639 100644 --- a/src/java/org/apache/cassandra/metrics/ClientRequestsMetrics.java +++ b/src/java/org/apache/cassandra/metrics/ClientRequestsMetrics.java @@ -32,6 +32,7 @@ public class ClientRequestsMetrics public final CASClientWriteRequestMetrics casWriteMetrics; public final CASClientRequestMetrics casReadMetrics; public final ViewWriteMetrics viewWriteMetrics; + public final AllRequestsMetrics allRequestsMetrics; private final Map readMetricsMap; private final Map writeMetricsMap; @@ -52,6 +53,7 @@ public ClientRequestsMetrics(String namePrefix) casWriteMetrics = new CASClientWriteRequestMetrics("CASWrite", namePrefix); casReadMetrics = new CASClientRequestMetrics("CASRead", namePrefix); viewWriteMetrics = new ViewWriteMetrics("ViewWrite", namePrefix); + allRequestsMetrics = new AllRequestsMetrics("All", namePrefix); readMetricsMap = new EnumMap<>(ConsistencyLevel.class); writeMetricsMap = new EnumMap<>(ConsistencyLevel.class); for (ConsistencyLevel level : ConsistencyLevel.values()) diff --git a/test/unit/org/apache/cassandra/metrics/ClientRequestsMetricsAllRequestsTest.java b/test/unit/org/apache/cassandra/metrics/ClientRequestsMetricsAllRequestsTest.java new file mode 100644 index 000000000000..fdd49fe45bc8 --- /dev/null +++ b/test/unit/org/apache/cassandra/metrics/ClientRequestsMetricsAllRequestsTest.java @@ -0,0 +1,130 @@ +/* + * 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.Collections; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.codahale.metrics.Meter; +import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.Session; +import org.apache.cassandra.cql3.CQLStatement; +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.cql3.QueryEvents; +import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.db.ConsistencyLevel; +import org.apache.cassandra.exceptions.ReadFailureException; +import org.apache.cassandra.exceptions.ReadTimeoutException; +import org.apache.cassandra.exceptions.UnavailableException; +import org.apache.cassandra.service.QueryState; + +import static org.junit.Assert.assertEquals; + +public class ClientRequestsMetricsAllRequestsTest extends CQLTester +{ + AllRequestsMetrics metrics = ClientRequestsMetricsProvider.instance.metrics(KEYSPACE).allRequestsMetrics; + + @Before + public void setup() + { + createTable("CREATE TABLE %s (id INT PRIMARY KEY, v TEXT)"); + } + + @After + public void teardown() + { + dropTable("DROP TABLE %s"); + } + + @Test + public void testInvalidRequest() + { + long before = metrics.invalid.getCount(); + try + { + executeNet("INSERT INTO %s (id, v) VALUES (1, ?)"); + } + catch (Throwable t) + { + // expected + } + assertEquals(1, metrics.invalid.getCount() - before); + } + + @Test + public void testInvalidPreparedRequest() + { + try(Session session = sessionNet()) + { + PreparedStatement prepare1 = session.prepare(formatQuery("DELETE FROM %s WHERE id = ?")); + BoundStatement bind = prepare1.bind(); + long before = metrics.invalid.getCount(); + try + { + session.execute(bind); + } + catch (Throwable t) + { + // expected + } + assertEquals(1, metrics.invalid.getCount() - before); + } + } + + @Test + public void testTimeoutRequest() + { + testNotifyQueryFailure(metrics.timeouts, new ReadTimeoutException(ConsistencyLevel.ONE)); + } + + @Test + public void testUnavailableRequest() + { + testNotifyQueryFailure(metrics.unavailables, UnavailableException.create(ConsistencyLevel.ONE, 1, 0)); + } + + @Test + public void testFailureRequest() + { + testNotifyQueryFailure(metrics.failures, new ReadFailureException(ConsistencyLevel.ONE, 0, 0, false, Collections.emptyMap())); + } + + @Test + public void testOtherErrorRequest() + { + testNotifyQueryFailure(metrics.otherErrors, new RuntimeException()); + } + + public void testNotifyQueryFailure(Meter meter, Exception exception) + { + CQLStatement cqlStatement = parseStatement("CREATE TABLE %s (id INT PRIMARY KEY, v TEXT)"); + long before = meter.getCount(); + + QueryEvents.instance.notifyQueryFailure(cqlStatement, cqlStatement.getRawCQLStatement(), QueryOptions.DEFAULT, + QueryState.forInternalCalls(), exception); + + assertEquals(1, meter.getCount() - before); + } + + +}