Skip to content

Commit

Permalink
Remove prepared statement from cache if error is thrown when reading …
Browse files Browse the repository at this point in the history
…the results. (#51)

Fix public access to NosqlDbConfig.setQueryCacheLifetime(int).
Add info log entry for creating cache (with params) and when query is removed from cache.
  • Loading branch information
cezarfx authored May 9, 2024
1 parent e8a15e2 commit 9660e3f
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,14 @@ public int getQueryCacheCapacity() {
}

/**
* Sets the capacity of the prepared query cache. By default this is set
* Sets the capacity of the prepared query cache. By default, this is set
* to {@link Constants#DEFAULT_QUERY_CACHE_CAPACITY}. The prepared query
* cache is controlled by both {@link #setQueryCacheCapacity(int)} and
* {@link #setQueryCacheLifetime(int)}. If capacity is 0 then entries are
* only ever removed because they expire. If lifetime is 0 then entries are
* only ever removed because the cache has reached capacity.
*/
NosqlDbConfig setQueryCacheCapacity(int capacity) {
public NosqlDbConfig setQueryCacheCapacity(int capacity) {
queryCacheCapacity = capacity;
return this;
}
Expand All @@ -159,14 +159,14 @@ public int getQueryCacheLifetime() {

/**
* Sets the lifetime of the prepared query cache in milliseconds. By
* default this is set to {@link Constants#DEFAULT_QUERY_CACHE_LIFETIME_MS}.
* default, this is set to {@link Constants#DEFAULT_QUERY_CACHE_LIFETIME_MS}.
* The prepared query cache is controlled by both
* {@link #setQueryCacheCapacity(int)} and
* {@link #setQueryCacheLifetime(int)}. If capacity is 0 then entries are
* only ever removed because they expire. If lifetime is 0 then entries are
* only ever removed because the cache has reached capacity.
*/
NosqlDbConfig setQueryCacheLifetime(int lifetime) {
public NosqlDbConfig setQueryCacheLifetime(int lifetime) {
queryCacheLifetime = lifetime;
return this;
}
Expand Down
21 changes: 14 additions & 7 deletions src/main/java/com/oracle/nosql/spring/data/core/IterableUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@

import oracle.nosql.driver.NoSQLException;
import oracle.nosql.driver.NoSQLHandle;
import oracle.nosql.driver.ops.PreparedStatement;
import oracle.nosql.driver.ops.QueryRequest;
import oracle.nosql.driver.ops.QueryResult;
import oracle.nosql.driver.util.LruCache;
import oracle.nosql.driver.values.MapValue;

import com.oracle.nosql.spring.data.core.convert.MappingNosqlConverter;
Expand All @@ -29,15 +31,12 @@
public class IterableUtil {

public static class IterableImpl implements Iterable<MapValue> {
final NoSQLHandle nosqlClient;
final QueryRequest queryRequest;
final IteratorImpl iter;

IterableImpl(NoSQLHandle nosqlClient,
LruCache<String, PreparedStatement> psCache,
QueryRequest queryRequest) {
this.nosqlClient = nosqlClient;
this.queryRequest = queryRequest;
this.iter = new IteratorImpl(nosqlClient, queryRequest);
this.iter = new IteratorImpl(nosqlClient, psCache, queryRequest);
}

@Override
Expand All @@ -52,13 +51,16 @@ public static class IteratorImpl implements Iterator<MapValue> {
LoggerFactory.getLogger(IteratorImpl.class);

final NoSQLHandle nosqlClient;
final LruCache<String, PreparedStatement> psCache;
final QueryRequest queryRequest;
QueryResult queryResult;
Iterator<MapValue> iterator;

IteratorImpl(NoSQLHandle nosqlClient,
LruCache<String, PreparedStatement> psCache,
QueryRequest queryRequest) {
this.nosqlClient = nosqlClient;
this.psCache = psCache;
this.queryRequest = queryRequest;
}

Expand Down Expand Up @@ -101,9 +103,14 @@ private boolean ensureIterator() {

return true;
} catch (NoSQLException nse) {
log.error("Query: {}",
queryRequest.getStatement());
String sql = queryRequest.getPreparedStatement() != null ?
queryRequest.getPreparedStatement().getSQLText() :
queryRequest.getStatement();
log.error("Query: {}", sql);
log.error(nse.getMessage());

psCache.remove(sql);
log.info("Removed from prepared statements cache: '{}'", sql);
throw MappingNosqlConverter.convert(nse);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,21 @@ protected NosqlTemplateBase(NosqlDbFactory nosqlDbFactory,
this.nosqlDbFactory = nosqlDbFactory;
nosqlClient = nosqlDbFactory.getNosqlClient();
this.mappingNosqlConverter = mappingNosqlConverter;
LOG.info("Create cache for prepared statements with capacity " +
nosqlDbFactory.getQueryCacheCapacity() + " items and lifetime " +
nosqlDbFactory.getQueryCacheLifetime() + " ms.");
psCache = new LruCache<>(nosqlDbFactory.getQueryCacheCapacity(),
nosqlDbFactory.getQueryCacheLifetime());
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}

protected TableResult doTableRequest(NosqlEntityInformation<?, ?> entityInformation,
protected TableResult doTableRequest(
NosqlEntityInformation<?, ?> entityInformation,
TableRequest tableReq) {

if (entityInformation != null &&
Expand Down Expand Up @@ -482,17 +487,17 @@ protected Iterable<MapValue> doRunQueryNosqlParams(
String query,
Map<String, FieldValue> nosqlParams) {

PreparedStatement preparedStatement =
PreparedStatement pStmt =
getPreparedStatement(entityInformation, query);

if (nosqlParams != null) {
for (Map.Entry<String, FieldValue> e : nosqlParams.entrySet()) {
preparedStatement.setVariable(e.getKey(), e.getValue());
pStmt.setVariable(e.getKey(), e.getValue());
}
}

QueryRequest qReq = new QueryRequest()
.setPreparedStatement(preparedStatement);
.setPreparedStatement(pStmt);

if (entityInformation != null) {
if (entityInformation.getTimeout() > 0) {
Expand All @@ -504,11 +509,12 @@ protected Iterable<MapValue> doRunQueryNosqlParams(

LOG.debug("Q: {}", query);
if (LOG.isDebugEnabled() && nosqlParams != null) {
for (Map.Entry<String, FieldValue> var : preparedStatement.getVariables().entrySet()) {
for (Map.Entry<String, FieldValue> var : pStmt.getVariables()
.entrySet()) {
LOG.debug(" {} = {}", var.getKey(), var.getValue());
}
}
return doQuery(qReq);
return doQuery(psCache, qReq);
}

protected <T> Iterable<MapValue> doExecuteMapValueQuery(NosqlQuery query,
Expand Down Expand Up @@ -552,14 +558,16 @@ protected <T> Iterable<MapValue> doExecuteMapValueQuery(NosqlQuery query,
LOG.debug("Q: {}", sql);
// System.out.println("Q: " + sql);
if (LOG.isDebugEnabled() && params != null) {
for (Map.Entry<String, FieldValue> var : pStmt.getVariables().entrySet()) {
for (Map.Entry<String, FieldValue> var : pStmt.getVariables()
.entrySet()) {
LOG.debug(" {} = {}", var.getKey(), var.getValue());
}
}
return doQuery(qReq);
return doQuery(psCache, qReq);
}

protected TableResult doGetTable(NosqlEntityInformation<?, ?> entityInformation) {
protected TableResult doGetTable(
NosqlEntityInformation<?, ?> entityInformation) {
try {
GetTableRequest request = new GetTableRequest();
request.setTableName(entityInformation.getTableName());
Expand Down Expand Up @@ -605,8 +613,9 @@ private PreparedStatement getPreparedStatement(
return preparedStatement.copyStatement();
}

private Iterable<MapValue> doQuery(QueryRequest qReq) {
return new IterableUtil.IterableImpl(nosqlClient, qReq);
private Iterable<MapValue> doQuery(
LruCache<String, PreparedStatement> psCache, QueryRequest qReq) {
return new IterableUtil.IterableImpl(nosqlClient, psCache, qReq);
}

private String getAutoGenType(NosqlEntityInformation<?, ?> entityInformation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.oracle.nosql.spring.data.core.NosqlOperations;
import com.oracle.nosql.spring.data.core.NosqlTemplate;
import com.oracle.nosql.spring.data.core.mapping.NosqlCapacityMode;
import com.oracle.nosql.spring.data.repository.support.NosqlEntityInformation;
import com.oracle.nosql.spring.data.test.app.Address;
import com.oracle.nosql.spring.data.test.app.AppConfig;
import com.oracle.nosql.spring.data.test.app.Customer;
Expand Down Expand Up @@ -674,4 +675,36 @@ private void printWithTypes(String name, Map<?, ?> map) {
}
}
}

@Test
public void testPrepStmtCacheRemove() throws ClassNotFoundException {
// todo: add invalidate prepared statements cache
// repo.invalidateCache();

// Use query, which will cache the prepared statement
List<Customer> customerList = repo.findByLastName("Smith");
Assert.assertEquals(0, customerList.size());

// Drop table
NosqlTemplate template = NosqlTemplate.create(AppConfig.nosqlDBConfig);

template.runTableRequest(
"DROP TABLE Customer");

try {
customerList = repo.findByLastName("Smith");
Assert.assertEquals(0, customerList.size());
Assert.fail("Should throw a 'Table not found' exception");
} catch (Exception e) {
Assert.assertEquals("Table not found: customer",
e.getCause().getMessage());
}

NosqlEntityInformation<Customer, ?> customerEntInfo =
template.getNosqlEntityInformation(Customer.class);
template.createTableIfNotExists(customerEntInfo);

customerList = repo.findByLastName("Smith");
Assert.assertEquals(0, customerList.size());
}
}

0 comments on commit 9660e3f

Please sign in to comment.