diff --git a/src/main/java/io/ebean/cache/ServerCacheNotification.java b/src/main/java/io/ebean/cache/ServerCacheNotification.java index 325003e9f..99c4340ef 100644 --- a/src/main/java/io/ebean/cache/ServerCacheNotification.java +++ b/src/main/java/io/ebean/cache/ServerCacheNotification.java @@ -10,22 +10,15 @@ import java.util.Set; */ public class ServerCacheNotification { - private final long modifyTimestamp; - private final Set dependentTables; - public ServerCacheNotification(long modifyTimestamp, Set dependentTables) { - this.modifyTimestamp = modifyTimestamp; + public ServerCacheNotification(Set dependentTables) { this.dependentTables = dependentTables; } @Override public String toString() { - return "ts:" + modifyTimestamp + " tables:" + dependentTables; - } - - public long getModifyTimestamp() { - return modifyTimestamp; + return "tables:" + dependentTables; } public Set getDependentTables() { diff --git a/src/main/java/io/ebeaninternal/api/SpiTransaction.java b/src/main/java/io/ebeaninternal/api/SpiTransaction.java index 0882d88ba..9c49f8e02 100644 --- a/src/main/java/io/ebeaninternal/api/SpiTransaction.java +++ b/src/main/java/io/ebeaninternal/api/SpiTransaction.java @@ -98,7 +98,7 @@ public interface SpiTransaction extends Transaction { /** * Return the start timestamp for the transaction (JVM side). */ - long getStartMillis(); + long getStartNanoTime(); /** * Return true if this transaction has updateAllLoadedProperties set. diff --git a/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java b/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java index 999ac7320..804fb7143 100644 --- a/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java +++ b/src/main/java/io/ebeaninternal/api/SpiTransactionProxy.java @@ -29,8 +29,8 @@ public abstract class SpiTransactionProxy implements SpiTransaction { } @Override - public long getStartMillis() { - return transaction.getStartMillis(); + public long getStartNanoTime() { + return transaction.getStartNanoTime(); } @Override diff --git a/src/main/java/io/ebeaninternal/api/TransactionEvent.java b/src/main/java/io/ebeaninternal/api/TransactionEvent.java index 348dd2a0f..86ee5f474 100644 --- a/src/main/java/io/ebeaninternal/api/TransactionEvent.java +++ b/src/main/java/io/ebeaninternal/api/TransactionEvent.java @@ -30,8 +30,6 @@ public class TransactionEvent implements Serializable { */ private final transient boolean local; - private final long startMillis; - private TransactionEventTable eventTables; private transient List> listenerNotify; @@ -43,8 +41,7 @@ public class TransactionEvent implements Serializable { /** * Create the TransactionEvent, one per Transaction. */ - public TransactionEvent(long startMillis) { - this.startMillis = startMillis; + public TransactionEvent() { this.local = true; } @@ -119,7 +116,7 @@ public class TransactionEvent implements Serializable { } if (changeSet == null) { - changeSet = new CacheChangeSet(manager.clockNowMillis()); + changeSet = new CacheChangeSet(); } if (eventTables != null && !eventTables.isEmpty()) { // notify cache with table based changes @@ -154,7 +151,7 @@ public class TransactionEvent implements Serializable { */ public CacheChangeSet obtainCacheChangeSet() { if (changeSet == null) { - changeSet = new CacheChangeSet(startMillis); + changeSet = new CacheChangeSet(); } return changeSet; } diff --git a/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java b/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java index 9a058444e..4fdde31ad 100644 --- a/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java +++ b/src/main/java/io/ebeaninternal/server/cache/CacheChangeSet.java @@ -28,13 +28,10 @@ public class CacheChangeSet { private final Map manyChangeMap = new HashMap<>(); - private final long modificationTimestamp; - /** * Construct specifying if we also need to process invalidation for entities based on views. */ - public CacheChangeSet(long modificationTimestamp) { - this.modificationTimestamp = modificationTimestamp; + public CacheChangeSet() { } /** @@ -172,13 +169,6 @@ public class CacheChangeSet { return manyChangeMap.computeIfAbsent(key, ManyChange::new); } - /** - * Return the modification timestamp for these changes. - */ - public long modificationTimestamp() { - return modificationTimestamp; - } - /** * Changes for a specific many property. */ diff --git a/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java index bfe2d756e..3d9486be9 100644 --- a/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/io/ebeaninternal/server/core/InternalConfiguration.java @@ -169,7 +169,7 @@ public class InternalConfiguration { this.online = online; this.serverConfig = serverConfig; this.clockService = new ClockService(serverConfig.getClock()); - this.tableModState = new TableModState(clockService); + this.tableModState = new TableModState(); this.logManager = initLogManager(); this.docStoreFactory = initDocStoreFactory(serverConfig.service(DocStoreFactory.class)); this.jsonFactory = serverConfig.getJsonFactory(); diff --git a/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java b/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java index 9fa93e693..b7543c76d 100644 --- a/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java +++ b/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java @@ -696,9 +696,7 @@ public final class OrmQueryRequest extends BeanRequest implements SpiOrmQuery } public void putToQueryCache(Object result) { - // use transaction start where as query statement start would be better at READ_COMMITTED - long asOfTimestamp = transaction.getStartMillis(); - beanDescriptor.queryCachePut(cacheKey, new QueryCacheEntry(result, dependentTables, asOfTimestamp)); + beanDescriptor.queryCachePut(cacheKey, new QueryCacheEntry(result, dependentTables, transaction.getStartNanoTime())); } /** diff --git a/src/main/java/io/ebeaninternal/server/transaction/ImplicitReadOnlyTransaction.java b/src/main/java/io/ebeaninternal/server/transaction/ImplicitReadOnlyTransaction.java index 12c13870f..9e0822e15 100644 --- a/src/main/java/io/ebeaninternal/server/transaction/ImplicitReadOnlyTransaction.java +++ b/src/main/java/io/ebeaninternal/server/transaction/ImplicitReadOnlyTransaction.java @@ -65,7 +65,6 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode private Map userObjects; private long startNanos; - private long startMillis; /** * Create without a tenantId. @@ -78,7 +77,6 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode this.connection = connection; this.persistenceContext = new DefaultPersistenceContext(); this.startNanos = System.nanoTime(); - this.startMillis = manager.clockNowMillis(); } /** @@ -90,9 +88,9 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode } @Override - public long getStartMillis() { + public long getStartNanoTime() { // not used on read only transaction - return startMillis; + return startNanos; } @Override diff --git a/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java b/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java index d9b153c16..5e1e258c2 100644 --- a/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java +++ b/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java @@ -185,7 +185,6 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes { protected ProfileLocation profileLocation; protected final long startNanos; - private final long startMillis; /** * Create a new JdbcTransaction. @@ -202,7 +201,6 @@ 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,7 +208,6 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes { this.batchOnCascadeMode = false; this.onQueryOnly = OnQueryOnly.ROLLBACK; } else { - this.startMillis = manager.clockNowMillis(); this.logSql = manager.isLogSql(); this.logSummary = manager.isLogSummary(); this.skipCacheAfterWrite = manager.isSkipCacheAfterWrite(); @@ -237,8 +234,8 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes { } @Override - public long getStartMillis() { - return startMillis; + public long getStartNanoTime() { + return startNanos; } @Override @@ -827,7 +824,7 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes { public TransactionEvent getEvent() { queryOnly = false; if (event == null) { - event = new TransactionEvent(startMillis); + event = new TransactionEvent(); } return event; } @@ -1024,7 +1021,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(startMillis); + event = new TransactionEvent(); } catch (Exception e) { doRollback(e); diff --git a/src/main/java/io/ebeaninternal/server/transaction/NoTransaction.java b/src/main/java/io/ebeaninternal/server/transaction/NoTransaction.java index e357e5a79..e9ae56065 100644 --- a/src/main/java/io/ebeaninternal/server/transaction/NoTransaction.java +++ b/src/main/java/io/ebeaninternal/server/transaction/NoTransaction.java @@ -46,9 +46,9 @@ class NoTransaction implements SpiTransaction { } @Override - public long getStartMillis() { + public long getStartNanoTime() { // not used - return System.currentTimeMillis(); + return System.nanoTime(); } @Override diff --git a/src/main/java/io/ebeaninternal/server/transaction/PostCommitProcessing.java b/src/main/java/io/ebeaninternal/server/transaction/PostCommitProcessing.java index 9797ef59f..77eec289d 100644 --- a/src/main/java/io/ebeaninternal/server/transaction/PostCommitProcessing.java +++ b/src/main/java/io/ebeaninternal/server/transaction/PostCommitProcessing.java @@ -145,9 +145,9 @@ final class PostCommitProcessing { if (cacheChanges != null) { Set touched = cacheChanges.touchedTables(); if (touched != null && !touched.isEmpty()) { - manager.processTouchedTables(touched, cacheChanges.modificationTimestamp()); + manager.processTouchedTables(touched); if (remoteTransactionEvent != null) { - remoteTransactionEvent.addRemoteTableMod(new RemoteTableMod(cacheChanges.modificationTimestamp(), touched)); + remoteTransactionEvent.addRemoteTableMod(new RemoteTableMod(touched)); } } cacheChanges.apply(); diff --git a/src/main/java/io/ebeaninternal/server/transaction/RemoteTableMod.java b/src/main/java/io/ebeaninternal/server/transaction/RemoteTableMod.java index 8e6316a58..f963aad71 100644 --- a/src/main/java/io/ebeaninternal/server/transaction/RemoteTableMod.java +++ b/src/main/java/io/ebeaninternal/server/transaction/RemoteTableMod.java @@ -15,7 +15,12 @@ public class RemoteTableMod implements BinaryWritable { private final Set tables; - public RemoteTableMod(long timestamp, Set tables) { + public RemoteTableMod(Set tables) { + this.tables = tables; + this.timestamp = System.currentTimeMillis(); + } + + private RemoteTableMod(long timestamp, Set tables) { this.timestamp = timestamp; this.tables = tables; } diff --git a/src/main/java/io/ebeaninternal/server/transaction/TableModState.java b/src/main/java/io/ebeaninternal/server/transaction/TableModState.java index 29b116aee..70d2ae6b2 100644 --- a/src/main/java/io/ebeaninternal/server/transaction/TableModState.java +++ b/src/main/java/io/ebeaninternal/server/transaction/TableModState.java @@ -4,7 +4,6 @@ import io.ebean.cache.QueryCacheEntry; import io.ebean.cache.QueryCacheEntryValidate; import io.ebean.cache.ServerCacheNotification; import io.ebean.cache.ServerCacheNotify; -import io.ebeaninternal.server.core.ClockService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,33 +21,31 @@ public class TableModState implements QueryCacheEntryValidate, ServerCacheNotify private static final Logger log = LoggerFactory.getLogger("io.ebean.cache.TABLEMOD"); - private final ClockService clockService; + private Map tableModStamp = new ConcurrentHashMap<>(); - private Map tableModStamp = new ConcurrentHashMap<>(); - - public TableModState(ClockService clockService) { - this.clockService = clockService; + public TableModState() { } /** * Set the modified timestamp on the tables that have been touched. */ - void touch(Set touchedTables, long modTimestamp) { + void touch(Set touchedTables) { + long modNanoTime = System.nanoTime(); for (String tableName : touchedTables) { - tableModStamp.put(tableName, modTimestamp); + tableModStamp.put(tableName, modNanoTime); } if (log.isDebugEnabled()) { - log.debug("TableModState updated - touched:{} modTimestamp:{}", touchedTables, modTimestamp); + log.debug("TableModState updated - touched:{} modNanoTime:{}", touchedTables, modNanoTime); } } /** * Return true if all the tables are valid based on timestamp comparison. */ - boolean isValid(Set tables, long sinceTimestamp) { + boolean isValid(Set tables, long sinceNanoTime) { for (String tableName : tables) { Long modTime = tableModStamp.get(tableName); - if (modTime != null && modTime >= sinceTimestamp ) { + if (modTime != null && modTime >= sinceNanoTime) { if (log.isTraceEnabled()) { log.trace("Invalidate on table:{}", tableName); } @@ -81,7 +78,7 @@ public class TableModState implements QueryCacheEntryValidate, ServerCacheNotify if (log.isDebugEnabled()) { log.debug("ServerCacheNotification:{}", notification); } - touch(notification.getDependentTables(), clockService.nowMillis()); + touch(notification.getDependentTables()); } /** @@ -97,6 +94,6 @@ public class TableModState implements QueryCacheEntryValidate, ServerCacheNotify if (log.isDebugEnabled()) { log.debug("RemoteTableMod:{}", tableMod); } - touch(tableMod.getTables(), clockService.nowMillis()); + touch(tableMod.getTables()); } } diff --git a/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java b/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java index 14d4e7d72..c1d5aadd9 100644 --- a/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java +++ b/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java @@ -452,7 +452,7 @@ public class TransactionManager implements SpiTransactionManager { private void externalModificationEvent(TransactionEventTable tableEvents) { - TransactionEvent event = new TransactionEvent(clockNowMillis()); + TransactionEvent event = new TransactionEvent(); event.add(tableEvents); PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, event); @@ -469,7 +469,7 @@ public class TransactionManager implements SpiTransactionManager { clusterLogger.debug("processing {}", remoteEvent); } - CacheChangeSet changeSet = new CacheChangeSet(clockNowMillis()); + CacheChangeSet changeSet = new CacheChangeSet(); RemoteTableMod tableMod = remoteEvent.getRemoteTableMod(); if (tableMod != null) { @@ -522,12 +522,12 @@ public class TransactionManager implements SpiTransactionManager { /** * Invalidate the query caches for entities based on views. */ - public void processTouchedTables(Set touchedTables, long modTimestamp) { - tableModState.touch(touchedTables, modTimestamp); + public void processTouchedTables(Set touchedTables) { + tableModState.touch(touchedTables); if (viewInvalidation) { beanDescriptorManager.processViewInvalidation(touchedTables); } - cacheNotify.notify(new ServerCacheNotification(modTimestamp, touchedTables)); + cacheNotify.notify(new ServerCacheNotification(touchedTables)); } /** diff --git a/src/test/java/io/ebeaninternal/server/cache/DefaultCacheHolderTest.java b/src/test/java/io/ebeaninternal/server/cache/DefaultCacheHolderTest.java index 3b8a42ded..d88f42719 100644 --- a/src/test/java/io/ebeaninternal/server/cache/DefaultCacheHolderTest.java +++ b/src/test/java/io/ebeaninternal/server/cache/DefaultCacheHolderTest.java @@ -4,13 +4,10 @@ import io.ebean.cache.ServerCacheFactory; import io.ebean.cache.ServerCacheOptions; import io.ebean.cache.ServerCacheType; import io.ebean.config.ServerConfig; -import io.ebeaninternal.server.core.ClockService; import io.ebeaninternal.server.transaction.TableModState; +import org.junit.Test; import org.tests.model.basic.Contact; import org.tests.model.basic.Customer; -import org.junit.Test; - -import java.time.Clock; import static org.assertj.core.api.Assertions.assertThat; @@ -25,7 +22,7 @@ public class DefaultCacheHolderTest { private CacheManagerOptions options() { return new CacheManagerOptions(null, new ServerConfig(), true) .with(defaultOptions, defaultOptions) - .with(cacheFactory, new TableModState(new ClockService(Clock.systemUTC()))); + .with(cacheFactory, new TableModState()); } diff --git a/src/test/java/io/ebeaninternal/server/cluster/binarymessage/BinaryTransactionEventReadWriteTest.java b/src/test/java/io/ebeaninternal/server/cluster/binarymessage/BinaryTransactionEventReadWriteTest.java index afeff01d3..9776ed6a5 100644 --- a/src/test/java/io/ebeaninternal/server/cluster/binarymessage/BinaryTransactionEventReadWriteTest.java +++ b/src/test/java/io/ebeaninternal/server/cluster/binarymessage/BinaryTransactionEventReadWriteTest.java @@ -41,11 +41,10 @@ public class BinaryTransactionEventReadWriteTest extends BaseTestCase { RemoteTransactionEvent event = new RemoteTransactionEvent("db"); - long timestamp = System.currentTimeMillis(); Set tables = new HashSet<>(); Collections.addAll(tables, "one", "two", "three"); - event.addRemoteTableMod(new RemoteTableMod(timestamp, tables)); + event.addRemoteTableMod(new RemoteTableMod(tables)); event.addRemoteCacheEvent(new RemoteCacheEvent(Customer.class)); event.addTableIUD(new TransactionEventTable.TableIUD("foo", true, false, true)); @@ -70,7 +69,6 @@ public class BinaryTransactionEventReadWriteTest extends BaseTestCase { // table mod RemoteTableMod remoteTableMod = read.getRemoteTableMod(); - assertThat(remoteTableMod.getTimestamp()).isEqualTo(timestamp); assertThat(remoteTableMod.getTables()).isEqualTo(tables); // Bean persist ids @@ -84,18 +82,15 @@ public class BinaryTransactionEventReadWriteTest extends BaseTestCase { RemoteTransactionEvent event = new RemoteTransactionEvent("db"); - long timestamp = System.currentTimeMillis(); Set tables = new HashSet<>(); Collections.addAll(tables, "one", "two", "three"); - event.addRemoteTableMod(new RemoteTableMod(timestamp, tables)); + event.addRemoteTableMod(new RemoteTableMod(tables)); byte[] binaryMessage = event.writeBinaryAsBytes(256); RemoteTransactionEvent read = reader.read(binaryMessage); RemoteTableMod remoteTableMod = read.getRemoteTableMod(); - - assertThat(remoteTableMod.getTimestamp()).isEqualTo(timestamp); assertThat(remoteTableMod.getTables()).isEqualTo(tables); } diff --git a/src/test/java/io/ebeaninternal/server/transaction/TableModStateTest.java b/src/test/java/io/ebeaninternal/server/transaction/TableModStateTest.java index 1fb80996c..f2ab59bd9 100644 --- a/src/test/java/io/ebeaninternal/server/transaction/TableModStateTest.java +++ b/src/test/java/io/ebeaninternal/server/transaction/TableModStateTest.java @@ -1,25 +1,26 @@ package io.ebeaninternal.server.transaction; -import io.ebeaninternal.server.core.ClockService; import org.junit.Test; -import java.time.Clock; import java.util.Collections; import java.util.HashSet; import java.util.Set; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class TableModStateTest { - private TableModState tableModState = new TableModState(new ClockService(Clock.systemUTC())); + private TableModState tableModState = new TableModState(); @Test public void isValid() { - long now = System.currentTimeMillis(); + long before = System.nanoTime(); - tableModState.touch(setOf("one", "two", "three"), now); + tableModState.touch(setOf("one", "two", "three")); + + long after = System.nanoTime(); // empty assertTrue(tableModState.isValid(Collections.emptySet(), 12L)); @@ -28,13 +29,13 @@ public class TableModStateTest { assertTrue(tableModState.isValid(setOf("noEntry"), 12L)); // later timestamp - assertTrue(tableModState.isValid(setOf("one"), now + 1)); - assertTrue(tableModState.isValid(setOf("one", "two", "noEntry"), now + 1)); + assertTrue(tableModState.isValid(setOf("one"), after)); + assertTrue(tableModState.isValid(setOf("one", "two", "noEntry"), after)); // invalid - assertFalse(tableModState.isValid(setOf("one"), now - 1)); - assertFalse(tableModState.isValid(setOf("one", "two"), now - 1)); - assertFalse(tableModState.isValid(setOf("three", "two"), now - 1)); + assertFalse(tableModState.isValid(setOf("one"), before)); + assertFalse(tableModState.isValid(setOf("one", "two"), before)); + assertFalse(tableModState.isValid(setOf("three", "two"), before)); } diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 9dfb9aab1..8d4ee5d7c 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -83,6 +83,7 @@ +