mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1427 - QueryCache should be cleared, if one of a dependent bean is updated
Initial work, does not include propagation across cluster.
This commit is contained in:
@@ -67,6 +67,7 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
private Map<String, Object> userObjects;
|
||||
|
||||
private long startNanos;
|
||||
private long startMillis;
|
||||
|
||||
/**
|
||||
* Create without a tenantId.
|
||||
@@ -79,6 +80,7 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
this.connection = connection;
|
||||
this.persistenceContext = new DefaultPersistenceContext();
|
||||
this.startNanos = System.nanoTime();
|
||||
this.startMillis = manager.clockNowEpoch();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,6 +91,12 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartMillis() {
|
||||
// not used on read only transaction
|
||||
return startMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
// do nothing
|
||||
|
||||
@@ -187,6 +187,7 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
protected ProfileLocation profileLocation;
|
||||
|
||||
protected final long startNanos;
|
||||
private final long startMillis;
|
||||
|
||||
/**
|
||||
* Create a new JdbcTransaction.
|
||||
@@ -203,6 +204,7 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
this.startNanos = System.nanoTime();
|
||||
|
||||
if (manager == null) {
|
||||
this.startMillis = System.currentTimeMillis();
|
||||
this.logSql = false;
|
||||
this.logSummary = false;
|
||||
this.skipCacheAfterWrite = true;
|
||||
@@ -210,6 +212,7 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
this.batchOnCascadeMode = PersistBatch.NONE;
|
||||
this.onQueryOnly = OnQueryOnly.ROLLBACK;
|
||||
} else {
|
||||
this.startMillis = manager.clockNowEpoch();
|
||||
this.logSql = manager.isLogSql();
|
||||
this.logSummary = manager.isLogSummary();
|
||||
this.skipCacheAfterWrite = manager.isSkipCacheAfterWrite();
|
||||
@@ -235,6 +238,11 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartMillis() {
|
||||
return startMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long profileOffset() {
|
||||
return (profileStream == null) ? 0 : profileStream.offset();
|
||||
@@ -853,7 +861,7 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
public TransactionEvent getEvent() {
|
||||
queryOnly = false;
|
||||
if (event == null) {
|
||||
event = new TransactionEvent();
|
||||
event = new TransactionEvent(startMillis);
|
||||
}
|
||||
return event;
|
||||
}
|
||||
@@ -1050,7 +1058,7 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
|
||||
// the event has been sent to the transaction manager
|
||||
// for postCommit processing (l2 cache updates etc)
|
||||
// start a new transaction event
|
||||
event = new TransactionEvent();
|
||||
event = new TransactionEvent(startMillis);
|
||||
|
||||
} catch (Exception e) {
|
||||
doRollback(e);
|
||||
|
||||
@@ -47,6 +47,12 @@ class NoTransaction implements SpiTransaction {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartMillis() {
|
||||
// not used
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
// always false
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Performs post commit processing using a background thread.
|
||||
@@ -88,10 +89,10 @@ public final class PostCommitProcessing {
|
||||
processTableEvents(event.getEventTables());
|
||||
if (manager.notifyL2CacheInForeground) {
|
||||
// process l2 cache changes in foreground
|
||||
processCacheChanges(event.buildCacheChanges(manager.viewInvalidation));
|
||||
processCacheChanges(event.buildCacheChanges());
|
||||
} else {
|
||||
// collect l2 cache changes for delayed background processing
|
||||
cacheChanges = event.buildCacheChanges(manager.viewInvalidation);
|
||||
cacheChanges = event.buildCacheChanges();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +166,12 @@ public final class PostCommitProcessing {
|
||||
*/
|
||||
private void processCacheChanges(CacheChangeSet cacheChanges) {
|
||||
if (cacheChanges != null) {
|
||||
manager.processViewInvalidation(cacheChanges.apply());
|
||||
Set<String> touched = cacheChanges.touchedTables();
|
||||
if (touched != null && !touched.isEmpty()) {
|
||||
manager.processTouchedTables(touched, cacheChanges.modificationTimestamp());
|
||||
// TODO: Propagate touched tables to other cluster members
|
||||
}
|
||||
cacheChanges.apply();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package io.ebeaninternal.server.transaction;
|
||||
|
||||
import io.ebean.cache.QueryCacheEntry;
|
||||
import io.ebean.cache.QueryCacheEntryValidate;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Holds timestamp of last modification per table.
|
||||
* <p>
|
||||
* This information is used to validate entries in the L2 query caches.
|
||||
* </p>
|
||||
*/
|
||||
public class TableModState implements QueryCacheEntryValidate {
|
||||
|
||||
private Map<String,Long> tableModStamp = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Set the modified timestamp on the tables that have been touched.
|
||||
*/
|
||||
public void touch(Set<String> touchedTables, long modTimestamp) {
|
||||
|
||||
for (String tableName : touchedTables) {
|
||||
tableModStamp.put(tableName, modTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if all the tables are valid based on timestamp comparison.
|
||||
*/
|
||||
public boolean isValid(Set<String> tables, long sinceTimestamp) {
|
||||
for (String tableName : tables) {
|
||||
Long modTime = tableModStamp.get(tableName);
|
||||
if (modTime != null && modTime > sinceTimestamp ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(QueryCacheEntry entry) {
|
||||
Set<String> dependentTables = entry.getDependentTables();
|
||||
if (dependentTables != null && !dependentTables.isEmpty()) {
|
||||
return isValid(dependentTables, entry.getTimestamp());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ import java.util.Set;
|
||||
/**
|
||||
* Manages transactions.
|
||||
* <p>
|
||||
* Keeps the Cache and Cluster in synch when transactions are committed.
|
||||
* Keeps the Cache and Cluster in sync when transactions are committed.
|
||||
* </p>
|
||||
*/
|
||||
public class TransactionManager implements SpiTransactionManager {
|
||||
@@ -136,6 +136,8 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
private final TimedMetricMap txnNamed;
|
||||
private final TransactionScopeManager scopeManager;
|
||||
|
||||
private final TableModState tableModState;
|
||||
|
||||
/**
|
||||
* Create the TransactionManager
|
||||
*/
|
||||
@@ -157,6 +159,7 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
this.clusterManager = options.clusterManager;
|
||||
this.serverName = options.config.getName();
|
||||
this.scopeManager = options.scopeManager;
|
||||
this.tableModState = options.tableModState;
|
||||
this.backgroundExecutor = options.backgroundExecutor;
|
||||
this.dataSourceSupplier = options.dataSourceSupplier;
|
||||
this.docStoreActive = options.config.getDocStoreConfig().isActive();
|
||||
@@ -178,6 +181,13 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
scopeManager.register(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the NOW timestamp in epoch millis.
|
||||
*/
|
||||
public long clockNowEpoch() {
|
||||
return System.currentTimeMillis(); // TODO: Review when we supply a Clock via ServerConfig.
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new scoped transaction.
|
||||
*/
|
||||
@@ -434,7 +444,7 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
|
||||
private void externalModificationEvent(TransactionEventTable tableEvents) {
|
||||
|
||||
TransactionEvent event = new TransactionEvent();
|
||||
TransactionEvent event = new TransactionEvent(clockNowEpoch());
|
||||
event.add(tableEvents);
|
||||
|
||||
PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, event);
|
||||
@@ -495,9 +505,10 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
/**
|
||||
* Invalidate the query caches for entities based on views.
|
||||
*/
|
||||
public void processViewInvalidation(Set<String> viewInvalidation) {
|
||||
if (!viewInvalidation.isEmpty()) {
|
||||
beanDescriptorManager.processViewInvalidation(viewInvalidation);
|
||||
public void processTouchedTables(Set<String> touchedTables, long modTimestamp) {
|
||||
tableModState.touch(touchedTables, modTimestamp);
|
||||
if (viewInvalidation) {
|
||||
beanDescriptorManager.processViewInvalidation(touchedTables);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,13 @@ public class TransactionManagerOptions {
|
||||
final SpiProfileHandler profileHandler;
|
||||
final TransactionScopeManager scopeManager;
|
||||
final SpiLogManager logManager;
|
||||
final TableModState tableModState;
|
||||
|
||||
|
||||
public TransactionManagerOptions(boolean notifyL2CacheInForeground, ServerConfig config, TransactionScopeManager scopeManager, ClusterManager clusterManager,
|
||||
BackgroundExecutor backgroundExecutor, DocStoreUpdateProcessor docStoreUpdateProcessor,
|
||||
BeanDescriptorManager descMgr, DataSourceSupplier dataSourceSupplier, SpiProfileHandler profileHandler, SpiLogManager logManager) {
|
||||
BeanDescriptorManager descMgr, DataSourceSupplier dataSourceSupplier, SpiProfileHandler profileHandler,
|
||||
SpiLogManager logManager, TableModState tableModState) {
|
||||
|
||||
this.notifyL2CacheInForeground = notifyL2CacheInForeground;
|
||||
this.config = config;
|
||||
@@ -39,6 +42,7 @@ public class TransactionManagerOptions {
|
||||
this.dataSourceSupplier = dataSourceSupplier;
|
||||
this.profileHandler = profileHandler;
|
||||
this.logManager = logManager;
|
||||
this.tableModState = tableModState;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user