io.ebeaninternal.server.transaction.TableModState Maven / Gradle / Ivy
package io.ebeaninternal.server.transaction;
import io.avaje.applog.AppLog;
import io.ebean.cache.QueryCacheEntry;
import io.ebean.cache.QueryCacheEntryValidate;
import io.ebean.cache.ServerCacheNotification;
import io.ebean.cache.ServerCacheNotify;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static java.lang.System.Logger.Level.DEBUG;
import static java.lang.System.Logger.Level.TRACE;
/**
* Holds timestamp of last modification per table.
*
* This information is used to validate entries in the L2 query caches.
*
*/
public final class TableModState implements QueryCacheEntryValidate, ServerCacheNotify {
private static final System.Logger log = AppLog.getLogger("io.ebean.cache.TABLEMOD");
private final Map tableModStamp = new ConcurrentHashMap<>();
public TableModState() {
}
/**
* Set the modified timestamp on the tables that have been touched.
*/
void touch(Set touchedTables) {
long modNanoTime = System.nanoTime();
for (String tableName : touchedTables) {
tableModStamp.put(tableName, modNanoTime);
}
if (log.isLoggable(DEBUG)) {
log.log(DEBUG, "TableModState updated - touched:{0} modNanoTime:{1}", touchedTables, modNanoTime);
}
}
/**
* Return true if all the tables are valid based on timestamp comparison.
*/
boolean isValid(Set tables, long sinceNanoTime) {
for (String tableName : tables) {
Long modTime = tableModStamp.get(tableName);
if (modTime != null && modTime >= sinceNanoTime) {
if (log.isLoggable(TRACE)) {
log.log(TRACE, "Invalidate on table:{0}", tableName);
}
return false;
}
}
return true;
}
@Override
public boolean isValid(QueryCacheEntry entry) {
Set dependentTables = entry.getDependentTables();
if (dependentTables != null && !dependentTables.isEmpty()) {
return isValid(dependentTables, entry.getTimestamp());
}
return true;
}
/**
* Update the table modification timestamps based on remote table modification events.
*
* Generally this is used with distributed caches (Hazelcast, Ignite etc) via topic.
*/
@Override
public void notify(ServerCacheNotification notification) {
// use local clock - for slightly more aggressive invalidation (as later)
// that removes any concern regarding clock syncing across cluster
if (log.isLoggable(DEBUG)) {
log.log(DEBUG, "ServerCacheNotification:{0}", notification);
}
touch(notification.getDependentTables());
}
/**
* Update from Remote transaction event.
*
* Generally this is used with Clustering (ebean-cluster, k8scache).
*/
public void notify(RemoteTableMod tableMod) {
// use local clock - for slightly more aggressive invalidation (as later)
// that removes any concern regarding clock syncing across cluster
if (log.isLoggable(DEBUG)) {
log.log(DEBUG, "RemoteTableMod:{0}", tableMod);
}
touch(tableMod.getTables());
}
}