/* * Copyright (C) 2012-2015 DataStax Inc. * * Licensed 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 com.datastax.driver.core; import org.scassandra.Scassandra; import org.scassandra.http.client.BatchExecution; import org.scassandra.http.client.PreparedStatementExecution; import org.scassandra.http.client.Query; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.util.List; import static com.datastax.driver.core.TestUtils.nonQuietClusterCloseOptions; import static org.testng.Assert.*; public class ConsistencyTest { private static final Logger logger = LoggerFactory.getLogger(ConsistencyTest.class); private ScassandraCluster sCluster; @BeforeClass(groups = "short") public void setUp() { sCluster = ScassandraCluster.builder().withNodes(1).build(); sCluster.init(); } @AfterClass(groups = "short") public void tearDownClass() { sCluster.stop(); } @AfterMethod(groups = "short") public void tearDown() { clearActivityLog(); } public void clearActivityLog() { for (Scassandra node : sCluster.nodes()) { node.activityClient().clearAllRecordedActivity(); } } public Cluster.Builder builder() { //Note: nonQuietClusterCloseOptions is used to speed up tests return Cluster.builder() .addContactPoints(sCluster.address(1).getAddress()) .withPort(sCluster.getBinaryPort()).withNettyOptions(nonQuietClusterCloseOptions); } /** * This method checks the expected/sent serial consistency level against that which is received. * ConsistencyLevel.SERIAL is the default serial consistency level, so even when sent it will return * as null. */ public void checkSerialCLMatch(ConsistencyLevel expected, String received) { if (expected.equals(ConsistencyLevel.SERIAL)) { assertNull(received); } else { assertTrue(received.equals(expected.toString())); } } public PreparedStatementExecution executePrepared(Session session, String statement, ConsistencyLevel level, ConsistencyLevel serialLevel) { PreparedStatement ps = session.prepare(statement); BoundStatement bound = ps.bind(); if (level != null) { bound.setConsistencyLevel(level); } if (serialLevel != null) { bound.setSerialConsistencyLevel(serialLevel); } session.execute(bound); List<PreparedStatementExecution> pses = sCluster.node(1).activityClient().retrievePreparedStatementExecutions(); PreparedStatementExecution pse = pses.get(0); assertTrue(pse.getPreparedStatementText().equals(statement)); return pse; } public BatchExecution executeBatch(Session session, String statement, ConsistencyLevel level, ConsistencyLevel serialLevel) { BatchStatement batch = new BatchStatement(); batch.add(new SimpleStatement(statement)); if (level != null) { batch.setConsistencyLevel(level); } if (serialLevel != null) { batch.setSerialConsistencyLevel(serialLevel); } session.execute(batch); List<BatchExecution> batches = sCluster.node(1).activityClient().retrieveBatches(); assertEquals(batches.size(), 1); return batches.get(0); } public Query executeSimple(Session session, String statement, ConsistencyLevel level, ConsistencyLevel serialLevel) { SimpleStatement simpleStatement = new SimpleStatement(statement); if (level != null) { simpleStatement.setConsistencyLevel(level); } if (serialLevel != null) { simpleStatement.setSerialConsistencyLevel(serialLevel); } session.execute(simpleStatement); //Find the unique query in the activity log. List<Query> queries = sCluster.node(1).activityClient().retrieveQueries(); for (Query query : queries) { if (query.getQuery().equals(statement)) return query; } return null; } /** * When no consistency level is defined the default of LOCAL_ONE should be used. * * @test_category consistency */ @Test(groups = "short") public void should_use_global_default_cl_when_none_specified() throws Throwable { //Build a cluster with no CL level set in the query options. Cluster cluster = builder().build(); try { Session session = cluster.connect(); //Construct unique simple statement query, with no CL defined. //Check to ensure String queryString = "default_cl"; Query clQuery = executeSimple(session, queryString, null, null); assertTrue(clQuery.getConsistency().equals(ConsistencyLevel.LOCAL_ONE.toString())); //Check prepared statement default CL String prepareString = "prepared_default_cl"; PreparedStatementExecution pse = executePrepared(session, prepareString, null, null); assertTrue(pse.getConsistency().equals(ConsistencyLevel.LOCAL_ONE.toString())); //Check batch statement default CL String batchStateString = "batch_default_cl"; BatchExecution batch = executeBatch(session, batchStateString, null, null); assertTrue(batch.getConsistency().equals(ConsistencyLevel.LOCAL_ONE.toString())); } finally { cluster.close(); } } /** * Exhaustively tests all consistency levels when they are set via QueryOptions. * * @test_category consistency */ @Test(groups = "short", dataProvider = "consistencyLevels", dataProviderClass = DataProviders.class) public void should_use_query_option_cl(ConsistencyLevel cl) throws Throwable { //Build a cluster with a CL level set in the query options. Cluster cluster = builder().withQueryOptions(new QueryOptions().setConsistencyLevel(cl)).build(); try { Session session = cluster.connect(); //Construct unique query, with no CL defined. String queryString = "query_cl"; Query clQuery = executeSimple(session, queryString, null, null); assertTrue(clQuery.getConsistency().equals(cl.toString())); //Check prepared statement CL String prepareString = "preapred_query_cl"; PreparedStatementExecution pse = executePrepared(session, prepareString, null, null); assertTrue(pse.getConsistency().equals(cl.toString())); //Check batch statement CL String batchStateString = "batch_query_cl"; BatchExecution batch = executeBatch(session, batchStateString, null, null); assertTrue(batch.getConsistency().equals(cl.toString())); } finally { cluster.close(); } } /** * Exhaustively tests all consistency levels when they are set at the statement level. * * @test_category consistency */ @Test(groups = "short", dataProvider = "consistencyLevels", dataProviderClass = DataProviders.class) public void should_use_statement_cl(ConsistencyLevel cl) throws Throwable { //Build a cluster with no CL set in the query options. //Note: nonQuietClusterCloseOptions is used to speed up tests Cluster cluster = builder().build(); try { Session session = cluster.connect(); //Construct unique query statement with a CL defined. String queryString = "statement_cl"; Query clQuery = executeSimple(session, queryString, cl, null); assertTrue(clQuery.getConsistency().equals(cl.toString())); //Check prepared statement CL String prepareString = "preapred_statement_cl"; PreparedStatementExecution pse = executePrepared(session, prepareString, cl, null); assertTrue(pse.getConsistency().equals(cl.toString())); //Check batch statement CL String batchStateString = "batch_statement_cl"; BatchExecution batch = executeBatch(session, batchStateString, cl, null); assertTrue(batch.getConsistency().equals(cl.toString())); } finally { cluster.close(); } } /** * Tests that order of precedence is followed when defining CLs. * Statement level CL should be honored above QueryOptions. * QueryOptions should be honored above default CL. * * @test_category consistency */ @Test(groups = "short") public void should_use_appropriate_cl_when_multiple_defined() throws Throwable { ConsistencyLevel cl_one = ConsistencyLevel.ONE; //Build a cluster with no CL set in the query options. Cluster cluster = builder().withQueryOptions(new QueryOptions().setConsistencyLevel(cl_one)).build(); try { Session session = cluster.connect(); //Check order of precedence for simple statements //Construct unique query statement with no CL defined. String queryString = "opts_cl"; Query clQuery = executeSimple(session, queryString, null, null); assertTrue(clQuery.getConsistency().equals(cl_one.toString())); //Construct unique query statement with a CL defined. ConsistencyLevel cl_all = ConsistencyLevel.ALL; queryString = "stm_cl"; clQuery = executeSimple(session, queryString, cl_all, null); assertTrue(clQuery.getConsistency().equals(cl_all.toString())); //Check order of precedence for prepared statements //Construct unique prepared statement with no CL defined. String prepareString = "prep_opts_cl"; PreparedStatementExecution pse = executePrepared(session, prepareString, null, null); assertTrue(pse.getConsistency().equals(cl_one.toString())); clearActivityLog(); //Construct unique prepared statement with a CL defined. prepareString = "prep_stm_cl"; pse = executePrepared(session, prepareString, cl_all, null); assertTrue(pse.getConsistency().equals(cl_all.toString())); //Check order of precedence for batch statements //Construct unique batch statement with no CL defined. String batchString = "batch_opts_cl"; BatchExecution batch = executeBatch(session, batchString, null, null); assertTrue(batch.getConsistency().equals(cl_one.toString())); clearActivityLog(); //Construct unique prepared statement with a CL defined. batchString = "prep_stm_cl"; batch = executeBatch(session, batchString, cl_all, null); assertTrue(batch.getConsistency().equals(cl_all.toString())); } finally { cluster.close(); } } /** * Exhaustively tests all serial consistency levels when they are set via QueryOptions. * * @test_category consistency */ @Test(groups = "short", dataProvider = "serialConsistencyLevels", dataProviderClass = DataProviders.class) public void should_use_query_option_serial_cl(ConsistencyLevel cl) throws Throwable { //Build a cluster with a CL level set in the query options. Cluster cluster = builder().withQueryOptions(new QueryOptions().setSerialConsistencyLevel(cl)).build(); try { Session session = cluster.connect(); //Construct unique query, with no CL defined. String queryString = "serial_query_cl"; Query clQuery = executeSimple(session, queryString, null, cl); checkSerialCLMatch(cl, clQuery.getSerialConsistency()); //Check prepared statement CL String prepareString = "preapred_statement_serial_cl"; PreparedStatementExecution pse = executePrepared(session, prepareString, null, null); checkSerialCLMatch(cl, pse.getSerialConsistency()); //Check batch statement CL String batchStateString = "batch_statement_serial_cl"; BatchExecution batch = executeBatch(session, batchStateString, null, null); checkSerialCLMatch(cl, batch.getSerialConsistency()); } finally { cluster.close(); } } /** * Exhaustively tests all serial consistency levels when they are set at the statement level. * * @test_category consistency */ @Test(groups = "short", dataProvider = "serialConsistencyLevels", dataProviderClass = DataProviders.class) public void should_use_statement_serial_cl(ConsistencyLevel cl) throws Throwable { //Build a cluster with no CL set in the query options. Cluster cluster = builder().build(); try { Session session = cluster.connect(); //Construct unique query statement with a CL defined. String queryString = "statement_serial_cl"; Query clQuery = executeSimple(session, queryString, null, cl); checkSerialCLMatch(cl, clQuery.getSerialConsistency()); //Check prepared statement CL String prepareString = "preapred_statement_serial_cl"; PreparedStatementExecution pse = executePrepared(session, prepareString, null, cl); checkSerialCLMatch(cl, pse.getSerialConsistency()); //Check batch statement CL String batchStateString = "batch_statement_serial_cl"; BatchExecution batch = executeBatch(session, batchStateString, null, cl); checkSerialCLMatch(cl, batch.getSerialConsistency()); } finally { cluster.close(); } } }