content: /*
   * 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.io.IOException;
  
  import org.junit.AfterClass;
  import org.junit.Assert;
  import org.junit.BeforeClass;
  import org.junit.Test;
  
  import com.datastax.driver.core.Cluster;
  import com.datastax.driver.core.PreparedStatement;
  import com.datastax.driver.core.Session;
  import com.datastax.driver.core.exceptions.InvalidQueryException;
  import org.apache.cassandra.ServerTestUtils;
  import org.apache.cassandra.config.DatabaseDescriptor;
  import org.apache.cassandra.cql3.QueryHandler;
  import org.apache.cassandra.cql3.QueryProcessor;
  import org.apache.cassandra.exceptions.ConfigurationException;
  import org.apache.cassandra.service.ClientState;
  import org.apache.cassandra.service.EmbeddedCassandraService;
  import org.apache.cassandra.utils.MD5Digest;
  
  import static org.apache.cassandra.cql3.CQLTester.assertRowsContains;
  import static org.apache.cassandra.cql3.CQLTester.row;
  import static org.apache.cassandra.metrics.CassandraMetricsRegistry.METRIC_SCOPE_UNDEFINED;
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertTrue;
  import static org.junit.Assert.fail;
  
  public class CQLMetricsTest
  {
      private static Cluster cluster;
      private static Session session;
      private static EmbeddedCassandraService cassandra;
  
      @BeforeClass()
      public static void setup() throws ConfigurationException, IOException
      {
          cassandra = ServerTestUtils.startEmbeddedCassandraService();
  
          cluster = Cluster.builder().addContactPoint("127.0.0.1").withPort(DatabaseDescriptor.getNativeTransportPort()).build();
          session = cluster.connect();
  
          session.execute("CREATE KEYSPACE IF NOT EXISTS junit WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };");
          session.execute("CREATE TABLE IF NOT EXISTS junit.metricstest (id int PRIMARY KEY, val text);");
      }
  
      @AfterClass
      public static void tearDown()
      {
          if (cluster != null)
              cluster.close();
          if (cassandra != null)
              cassandra.stop();
      }
  
      @Test
      public void testConnectionWithUseDisabled()
      {
          long useCountBefore = QueryProcessor.metrics.useStatementsExecuted.getCount();
          DatabaseDescriptor.setUseStatementsEnabled(false);
  
          try (Session ignored = cluster.connect("junit"))
          {
              fail("expected USE statement to fail with use_statements_enabled = false");
          }
          catch (InvalidQueryException e)
          {
              Assert.assertEquals(useCountBefore, QueryProcessor.metrics.useStatementsExecuted.getCount());
              assertTrue(e.getMessage().contains("USE statements prohibited"));
          }
          finally
          {
              DatabaseDescriptor.setUseStatementsEnabled(true);
          }
  
          assertRowsContains(cluster, session.execute("SELECT * FROM system_metrics.cql_group"),
                  row("org.apache.cassandra.metrics.CQL.UseStatementsExecuted", METRIC_SCOPE_UNDEFINED, "counter",
                          String.valueOf(useCountBefore)));
      }
  
      @Test
      public void testPreparedStatementsCountWithUse()
      {
          int n = QueryProcessor.metrics.preparedStatementsCount.getValue();
          // Use local test session to avoid interference with other tests due to CASSANDRA-17248
          try (Session localSession = cluster.connect())
          {
              long useCountBefore = QueryProcessor.metrics.useStatementsExecuted.getCount();
              localSession.execute("use junit");
              Assert.assertEquals(useCountBefore + 1, QueryProcessor.metrics.useStatementsExecuted.getCount());
              localSession.prepare("SELECT * FROM junit.metricstest WHERE id = ?");
              assertEquals(n + 2, (int) QueryProcessor.metrics.preparedStatementsCount.getValue());
  
              assertRowsContains(cluster, session.execute("SELECT * FROM system_metrics.cql_group"),
                                 row("org.apache.cassandra.metrics.CQL.PreparedStatementsCount", METRIC_SCOPE_UNDEFINED, "gauge",
                                     String.valueOf(QueryProcessor.metrics.preparedStatementsCount.getValue())));
          }
      }
  
      @Test
      public void testPreparedStatementsCountWithoutUse()
      {
          int n = QueryProcessor.metrics.preparedStatementsCount.getValue();
          long useCountBefore = QueryProcessor.metrics.useStatementsExecuted.getCount();
          session.prepare("SELECT * FROM junit.metricstest WHERE id = 101");
          Assert.assertEquals(useCountBefore, QueryProcessor.metrics.useStatementsExecuted.getCount());
          assertEquals(n+1, (int) QueryProcessor.metrics.preparedStatementsCount.getValue());
      }
  
      @Test
      public void testPreparedStatementCacheMemoryUsed()
      {
          long n = QueryProcessor.metrics.preparedStatementsCacheSize.getValue();
          String rawQueryString = "SELECT id, val FROM junit.metricstest WHERE id = 100";
          PreparedStatement s = session.prepare(rawQueryString);
  
          // Compute the expected size of a prepared statement.
          QueryHandler.Prepared expected = QueryProcessor.parseAndPrepare(rawQueryString, ClientState.forInternalCalls(), false);
          MD5Digest hashValue = MD5Digest.compute(rawQueryString);
          long expectedHashSize = hashValue.size();
          long expectedStatementSize = expected.pstmntSize;
          long expectedSize = expectedHashSize + expectedStatementSize;
  
          long actualSize = QueryProcessor.metrics.preparedStatementsCacheSize.getValue() - n;
          assertEquals(expectedSize, actualSize);
      }
  
      @Test
      public void testRegularStatementsExecuted()
      {
          clearMetrics();
          PreparedStatement metricsStatement = session.prepare("INSERT INTO junit.metricstest (id, val) VALUES (?, ?)");
  
          assertEquals(0, QueryProcessor.metrics.preparedStatementsExecuted.getCount());
          assertEquals(0, QueryProcessor.metrics.regularStatementsExecuted.getCount());
  
          for (int i = 0; i < 10; i++)
              session.execute(String.format("INSERT INTO junit.metricstest (id, val) VALUES (%d, '%s')", i, "val" + i));
  
          assertEquals(0, QueryProcessor.metrics.preparedStatementsExecuted.getCount());
          assertEquals(10, QueryProcessor.metrics.regularStatementsExecuted.getCount());
      }
  
      @Test
      public void testPreparedStatementsExecuted()
      {
          clearMetrics();
          PreparedStatement metricsStatement = session.prepare("INSERT INTO junit.metricstest (id, val) VALUES (?, ?)");
  
          assertEquals(0, QueryProcessor.metrics.preparedStatementsExecuted.getCount());
          assertEquals(0, QueryProcessor.metrics.regularStatementsExecuted.getCount());
  
          for (int i = 0; i < 10; i++)
              session.execute(metricsStatement.bind(i, "val" + i));
  
          assertEquals(10, QueryProcessor.metrics.preparedStatementsExecuted.getCount());
          assertEquals(0, QueryProcessor.metrics.regularStatementsExecuted.getCount());
  
          assertRowsContains(cluster, session.execute("SELECT * FROM system_metrics.cql_group"),
                  row("org.apache.cassandra.metrics.CQL.RegularStatementsExecuted", METRIC_SCOPE_UNDEFINED, "counter",
                          String.valueOf(QueryProcessor.metrics.regularStatementsExecuted.getCount())),
                  row("org.apache.cassandra.metrics.CQL.PreparedStatementsExecuted", METRIC_SCOPE_UNDEFINED, "counter",
                          String.valueOf(QueryProcessor.metrics.preparedStatementsExecuted.getCount())));
      }
  
      @Test
      public void testPreparedStatementsRatio()
      {
          clearMetrics();
          PreparedStatement metricsStatement = session.prepare("INSERT INTO junit.metricstest (id, val) VALUES (?, ?)");
  
          assertEquals(Double.NaN, QueryProcessor.metrics.preparedStatementsRatio.getValue(), 0.0);
  
          for (int i = 0; i < 10; i++)
              session.execute(metricsStatement.bind(i, "val" + i));
          assertEquals(1.0, QueryProcessor.metrics.preparedStatementsRatio.getValue(), 0.0);
  
          for (int i = 0; i < 10; i++)
              session.execute(String.format("INSERT INTO junit.metricstest (id, val) VALUES (%d, '%s')", i, "val" + i));
          assertEquals(0.5, QueryProcessor.metrics.preparedStatementsRatio.getValue(), 0.0);
  
          assertRowsContains(cluster, session.execute("SELECT * FROM system_metrics.cql_group"),
                  row("org.apache.cassandra.metrics.CQL.PreparedStatementsRatio", METRIC_SCOPE_UNDEFINED, "gauge",
                          String.valueOf(QueryProcessor.metrics.preparedStatementsRatio.getValue())));
      }
  
      private void clearMetrics()
      {
          QueryProcessor.metrics.preparedStatementsExecuted.dec(QueryProcessor.metrics.preparedStatementsExecuted.getCount());
          QueryProcessor.metrics.regularStatementsExecuted.dec(QueryProcessor.metrics.regularStatementsExecuted.getCount());
          QueryProcessor.metrics.preparedStatementsEvicted.dec(QueryProcessor.metrics.preparedStatementsEvicted.getCount());
      }
  }
  
  