Merge branch 'DATACASS-139' into 1.0.x
* DATACASS-139: DATACASS-139 - IN PROGRESS - Added PreparedStatement Cache for Performance Requirement.
This commit is contained in:
@@ -114,6 +114,11 @@
|
|||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
<version>15.0</version>
|
<version>15.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.liquibase</groupId>
|
||||||
|
<artifactId>liquibase-core</artifactId>
|
||||||
|
<version>3.1.1</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2014 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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 org.springframework.cassandra.core;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import com.datastax.driver.core.PreparedStatement;
|
||||||
|
import com.datastax.driver.core.Session;
|
||||||
|
import com.datastax.driver.core.exceptions.DriverException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Prepared Statement Creator maintains a cache of all prepared statements for the duration of this life of the
|
||||||
|
* container.
|
||||||
|
*
|
||||||
|
* When preparing statements with Cassandra, each Statement should be prepared once and only once due to the overhead of
|
||||||
|
* preparing the statement.
|
||||||
|
*
|
||||||
|
* @author David Webb
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class CachedPreparedStatementCreator implements PreparedStatementCreator {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(CachedPreparedStatementCreator.class);
|
||||||
|
|
||||||
|
private final String cql;
|
||||||
|
|
||||||
|
private static final Map<Session, Map<String, PreparedStatement>> psMap = new ConcurrentHashMap<Session, Map<String, PreparedStatement>>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a PreparedStatementCreator from the provided CQL.
|
||||||
|
*
|
||||||
|
* @param cql
|
||||||
|
*/
|
||||||
|
public CachedPreparedStatementCreator(String cql) {
|
||||||
|
Assert.notNull(cql, "CQL is required to create a PreparedStatement");
|
||||||
|
this.cql = cql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCql() {
|
||||||
|
return this.cql;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PreparedStatement createPreparedStatement(Session session) throws DriverException {
|
||||||
|
|
||||||
|
StringBuilder keyspaceCQLKey = new StringBuilder().append(session.getLoggedKeyspace()).append("|").append(this.cql);
|
||||||
|
|
||||||
|
log.debug(String.format("Cachable PreparedStatement in Keyspace [%s]", session.getLoggedKeyspace()));
|
||||||
|
|
||||||
|
Map<String, PreparedStatement> sessionMap = psMap.get(session);
|
||||||
|
if (sessionMap == null) {
|
||||||
|
sessionMap = new ConcurrentHashMap<String, PreparedStatement>();
|
||||||
|
psMap.put(session, sessionMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
PreparedStatement pstmt = sessionMap.get(keyspaceCQLKey.toString());
|
||||||
|
if (pstmt == null) {
|
||||||
|
log.debug("No Cached PreparedStatement found...Creating and Caching");
|
||||||
|
pstmt = session.prepare(this.cql);
|
||||||
|
sessionMap.put(keyspaceCQLKey.toString(), pstmt);
|
||||||
|
} else {
|
||||||
|
log.debug("Found cached PreparedStatement");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pstmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -737,7 +737,7 @@ public interface CqlOperations {
|
|||||||
List<Map<String, Object>> processListOfMap(ResultSet resultSet) throws DataAccessException;
|
List<Map<String, Object>> processListOfMap(ResultSet resultSet) throws DataAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the CQL provided into a {@link SimplePreparedStatementCreator}. <b>This can only be used for CQL
|
* Converts the CQL provided into a {@link CachedPreparedStatementCreator}. <b>This can only be used for CQL
|
||||||
* Statements that do not have data binding.</b> The results of the PreparedStatement are processed with
|
* Statements that do not have data binding.</b> The results of the PreparedStatement are processed with
|
||||||
* PreparedStatementCallback implementation provided by the Application Code.
|
* PreparedStatementCallback implementation provided by the Application Code.
|
||||||
*
|
*
|
||||||
@@ -762,7 +762,7 @@ public interface CqlOperations {
|
|||||||
<T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) throws DataAccessException;
|
<T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) throws DataAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
|
* Converts the CQL provided into a {@link CachedPreparedStatementCreator}. Then, the PreparedStatementBinder will
|
||||||
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
|
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
|
||||||
* processed with the ResultSetExtractor implementation provided by the Application Code. The can return any object,
|
* processed with the ResultSetExtractor implementation provided by the Application Code. The can return any object,
|
||||||
* including a List of Objects to support the ResultSet processing.
|
* including a List of Objects to support the ResultSet processing.
|
||||||
@@ -776,7 +776,7 @@ public interface CqlOperations {
|
|||||||
<T> T query(String cql, PreparedStatementBinder psb, ResultSetExtractor<T> rse) throws DataAccessException;
|
<T> T query(String cql, PreparedStatementBinder psb, ResultSetExtractor<T> rse) throws DataAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
|
* Converts the CQL provided into a {@link CachedPreparedStatementCreator}. Then, the PreparedStatementBinder will
|
||||||
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
|
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
|
||||||
* processed with the ResultSetExtractor implementation provided by the Application Code. The can return any object,
|
* processed with the ResultSetExtractor implementation provided by the Application Code. The can return any object,
|
||||||
* including a List of Objects to support the ResultSet processing.
|
* including a List of Objects to support the ResultSet processing.
|
||||||
@@ -792,7 +792,7 @@ public interface CqlOperations {
|
|||||||
throws DataAccessException;
|
throws DataAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
|
* Converts the CQL provided into a {@link CachedPreparedStatementCreator}. Then, the PreparedStatementBinder will
|
||||||
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
|
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
|
||||||
* processed with the RowCallbackHandler implementation provided and nothing is returned.
|
* processed with the RowCallbackHandler implementation provided and nothing is returned.
|
||||||
*
|
*
|
||||||
@@ -804,7 +804,7 @@ public interface CqlOperations {
|
|||||||
void query(String cql, PreparedStatementBinder psb, RowCallbackHandler rch) throws DataAccessException;
|
void query(String cql, PreparedStatementBinder psb, RowCallbackHandler rch) throws DataAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
|
* Converts the CQL provided into a {@link CachedPreparedStatementCreator}. Then, the PreparedStatementBinder will
|
||||||
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
|
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
|
||||||
* processed with the RowCallbackHandler implementation provided and nothing is returned.
|
* processed with the RowCallbackHandler implementation provided and nothing is returned.
|
||||||
*
|
*
|
||||||
@@ -818,7 +818,7 @@ public interface CqlOperations {
|
|||||||
throws DataAccessException;
|
throws DataAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
|
* Converts the CQL provided into a {@link CachedPreparedStatementCreator}. Then, the PreparedStatementBinder will
|
||||||
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
|
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
|
||||||
* processed with the RowMapper implementation provided and a List is returned with elements of Type <T> for each Row
|
* processed with the RowMapper implementation provided and a List is returned with elements of Type <T> for each Row
|
||||||
* returned.
|
* returned.
|
||||||
@@ -832,7 +832,7 @@ public interface CqlOperations {
|
|||||||
<T> List<T> query(String cql, PreparedStatementBinder psb, RowMapper<T> rowMapper) throws DataAccessException;
|
<T> List<T> query(String cql, PreparedStatementBinder psb, RowMapper<T> rowMapper) throws DataAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
|
* Converts the CQL provided into a {@link CachedPreparedStatementCreator}. Then, the PreparedStatementBinder will
|
||||||
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
|
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
|
||||||
* processed with the RowMapper implementation provided and a List is returned with elements of Type <T> for each Row
|
* processed with the RowMapper implementation provided and a List is returned with elements of Type <T> for each Row
|
||||||
* returned.
|
* returned.
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.cassandra.core;
|
package org.springframework.cassandra.core;
|
||||||
|
|
||||||
|
import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -72,8 +74,6 @@ import com.datastax.driver.core.querybuilder.Select;
|
|||||||
import com.datastax.driver.core.querybuilder.Truncate;
|
import com.datastax.driver.core.querybuilder.Truncate;
|
||||||
import com.datastax.driver.core.querybuilder.Update;
|
import com.datastax.driver.core.querybuilder.Update;
|
||||||
|
|
||||||
import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>This is the Central class in the Cassandra core package.</b> It simplifies the use of Cassandra and helps to avoid
|
* <b>This is the Central class in the Cassandra core package.</b> It simplifies the use of Cassandra and helps to avoid
|
||||||
* common errors. It executes the core Cassandra workflow, leaving application code to provide CQL and result
|
* common errors. It executes the core Cassandra workflow, leaving application code to provide CQL and result
|
||||||
@@ -843,7 +843,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T execute(String cql, PreparedStatementCallback<T> action) {
|
public <T> T execute(String cql, PreparedStatementCallback<T> action) {
|
||||||
return execute(new SimplePreparedStatementCreator(cql), action);
|
return execute(new CachedPreparedStatementCreator(cql), action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -882,7 +882,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
|
|||||||
@Override
|
@Override
|
||||||
public <T> T query(String cql, PreparedStatementBinder psb, ResultSetExtractor<T> rse, QueryOptions options)
|
public <T> T query(String cql, PreparedStatementBinder psb, ResultSetExtractor<T> rse, QueryOptions options)
|
||||||
throws DataAccessException {
|
throws DataAccessException {
|
||||||
return query(new SimplePreparedStatementCreator(cql), psb, rse, options);
|
return query(new CachedPreparedStatementCreator(cql), psb, rse, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -893,7 +893,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
|
|||||||
@Override
|
@Override
|
||||||
public void query(String cql, PreparedStatementBinder psb, RowCallbackHandler rch, QueryOptions options)
|
public void query(String cql, PreparedStatementBinder psb, RowCallbackHandler rch, QueryOptions options)
|
||||||
throws DataAccessException {
|
throws DataAccessException {
|
||||||
query(new SimplePreparedStatementCreator(cql), psb, rch, options);
|
query(new CachedPreparedStatementCreator(cql), psb, rch, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -904,7 +904,7 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
|
|||||||
@Override
|
@Override
|
||||||
public <T> List<T> query(String cql, PreparedStatementBinder psb, RowMapper<T> rowMapper, QueryOptions options)
|
public <T> List<T> query(String cql, PreparedStatementBinder psb, RowMapper<T> rowMapper, QueryOptions options)
|
||||||
throws DataAccessException {
|
throws DataAccessException {
|
||||||
return query(new SimplePreparedStatementCreator(cql), psb, rowMapper, options);
|
return query(new CachedPreparedStatementCreator(cql), psb, rowMapper, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -915,11 +915,13 @@ public class CqlTemplate extends CassandraAccessor implements CqlOperations {
|
|||||||
@Override
|
@Override
|
||||||
public void ingest(String cql, RowIterator rowIterator, WriteOptions options) {
|
public void ingest(String cql, RowIterator rowIterator, WriteOptions options) {
|
||||||
|
|
||||||
PreparedStatement preparedStatement = getSession().prepare(cql);
|
CachedPreparedStatementCreator cpsc = new CachedPreparedStatementCreator(cql);
|
||||||
|
|
||||||
|
PreparedStatement preparedStatement = cpsc.createPreparedStatement(getSession());
|
||||||
addPreparedStatementOptions(preparedStatement, options);
|
addPreparedStatementOptions(preparedStatement, options);
|
||||||
|
|
||||||
while (rowIterator.hasNext()) {
|
while (rowIterator.hasNext()) {
|
||||||
getSession().execute(preparedStatement.bind(rowIterator.next()));
|
getSession().executeAsync(preparedStatement.bind(rowIterator.next()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2014 the original author or authors.
|
|
||||||
*
|
|
||||||
* 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 org.springframework.cassandra.core;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.datastax.driver.core.BoundStatement;
|
|
||||||
import com.datastax.driver.core.PreparedStatement;
|
|
||||||
import com.datastax.driver.core.Session;
|
|
||||||
import com.datastax.driver.core.exceptions.DriverException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author David Webb
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class PreparedStatementCreatorImpl implements PreparedStatementCreator, PreparedStatementBinder {
|
|
||||||
|
|
||||||
private final String cql;
|
|
||||||
private List<Object> values;
|
|
||||||
|
|
||||||
public PreparedStatementCreatorImpl(String cql) {
|
|
||||||
this.cql = cql;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PreparedStatementCreatorImpl(String cql, List<Object> values) {
|
|
||||||
this.cql = cql;
|
|
||||||
this.values = values;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.springframework.cassandra.core.PreparedStatementSetter#setValues(com.datastax.driver.core.PreparedStatement)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public BoundStatement bindValues(PreparedStatement ps) throws DriverException {
|
|
||||||
// Nothing to set if there are no values
|
|
||||||
if (values == null) {
|
|
||||||
return new BoundStatement(ps);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ps.bind(values.toArray());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCql() {
|
|
||||||
return this.cql;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.springframework.cassandra.core.PreparedStatementCreator#createPreparedStatement(com.datastax.driver.core.Session)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public PreparedStatement createPreparedStatement(Session session) throws DriverException {
|
|
||||||
return session.prepare(this.cql);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -22,6 +22,12 @@ import com.datastax.driver.core.Session;
|
|||||||
import com.datastax.driver.core.exceptions.DriverException;
|
import com.datastax.driver.core.exceptions.DriverException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* This Prepared Statement Creator simply prepares a statement from the CQL string. This should not be used in
|
||||||
|
* Production systems with high volume reads and writes. Use {@link CachedPreparedStatementCreator}
|
||||||
|
*
|
||||||
|
* When preparing statements with Cassandra, each Statement should be prepared once and only once due to the overhead of
|
||||||
|
* preparing the statement.
|
||||||
|
*
|
||||||
* @author David Webb
|
* @author David Webb
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -43,9 +49,6 @@ public class SimplePreparedStatementCreator implements PreparedStatementCreator
|
|||||||
return this.cql;
|
return this.cql;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.springframework.cassandra.core.PreparedStatementCreator#createPreparedStatement(com.datastax.driver.core.Session)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public PreparedStatement createPreparedStatement(Session session) throws DriverException {
|
public PreparedStatement createPreparedStatement(Session session) throws DriverException {
|
||||||
return session.prepare(this.cql);
|
return session.prepare(this.cql);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<logger name="org.springframework" level="info" />
|
<logger name="org.springframework" level="info" />
|
||||||
<logger name="org.springframework.cassandra" level="info" />
|
<logger name="org.springframework.cassandra" level="debug" />
|
||||||
<logger name="com.datastax" level="info" />
|
<logger name="com.datastax" level="info" />
|
||||||
|
|
||||||
<root level="warn">
|
<root level="warn">
|
||||||
|
|||||||
Reference in New Issue
Block a user