Honor expireAfterWrite in the H2CacheImpl

The H2CacheImpl didn't honor the expireAfterWrite. This had an effect
that persisted entries never expired. For example, the web_sessions
cache entries didn't expire after their maxAge was reached.

Change-Id: I3a7c754ee05fe9ac92d96652db4b862bb597eba1
Signed-off-by: Saša Živkov <sasa.zivkov@sap.com>
Signed-off-by: Adrian Görler <adrian.goerler@sap.com>
This commit is contained in:
Saša Živkov
2014-08-06 14:54:33 +02:00
committed by Adrian Görler
parent c07d0d0f04
commit 98adfd5c53
2 changed files with 31 additions and 7 deletions

View File

@@ -161,7 +161,8 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener {
return defaultFactory.build(def); return defaultFactory.build(def);
} }
SqlStore<K, V> store = newSqlStore(def.name(), def.keyType(), limit); SqlStore<K, V> store = newSqlStore(def.name(), def.keyType(), limit,
def.expireAfterWrite(TimeUnit.SECONDS));
H2CacheImpl<K, V> cache = new H2CacheImpl<K, V>( H2CacheImpl<K, V> cache = new H2CacheImpl<K, V>(
executor, store, def.keyType(), executor, store, def.keyType(),
(Cache<K, ValueHolder<V>>) defaultFactory.create(def, true).build()); (Cache<K, ValueHolder<V>>) defaultFactory.create(def, true).build());
@@ -182,7 +183,8 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener {
return defaultFactory.build(def, loader); return defaultFactory.build(def, loader);
} }
SqlStore<K, V> store = newSqlStore(def.name(), def.keyType(), limit); SqlStore<K, V> store = newSqlStore(def.name(), def.keyType(), limit,
def.expireAfterWrite(TimeUnit.SECONDS));
Cache<K, ValueHolder<V>> mem = (Cache<K, ValueHolder<V>>) Cache<K, ValueHolder<V>> mem = (Cache<K, ValueHolder<V>>)
defaultFactory.create(def, true) defaultFactory.create(def, true)
.build((CacheLoader<K, V>) new H2CacheImpl.Loader<K, V>( .build((CacheLoader<K, V>) new H2CacheImpl.Loader<K, V>(
@@ -209,9 +211,11 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener {
private <V, K> SqlStore<K, V> newSqlStore( private <V, K> SqlStore<K, V> newSqlStore(
String name, String name,
TypeLiteral<K> keyType, TypeLiteral<K> keyType,
long maxSize) { long maxSize,
Long expireAfterWrite) {
File db = new File(cacheDir, name).getAbsoluteFile(); File db = new File(cacheDir, name).getAbsoluteFile();
String url = "jdbc:h2:" + db.toURI().toString(); String url = "jdbc:h2:" + db.toURI().toString();
return new SqlStore<>(url, keyType, maxSize); return new SqlStore<>(url, keyType, maxSize,
expireAfterWrite == null ? 0 : expireAfterWrite.longValue());
} }
} }

View File

@@ -313,16 +313,19 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> implements
private final String url; private final String url;
private final KeyType<K> keyType; private final KeyType<K> keyType;
private final long maxSize; private final long maxSize;
private final long expireAfterWrite;
private final BlockingQueue<SqlHandle> handles; private final BlockingQueue<SqlHandle> handles;
private final AtomicLong hitCount = new AtomicLong(); private final AtomicLong hitCount = new AtomicLong();
private final AtomicLong missCount = new AtomicLong(); private final AtomicLong missCount = new AtomicLong();
private volatile BloomFilter<K> bloomFilter; private volatile BloomFilter<K> bloomFilter;
private int estimatedSize; private int estimatedSize;
SqlStore(String jdbcUrl, TypeLiteral<K> keyType, long maxSize) { SqlStore(String jdbcUrl, TypeLiteral<K> keyType, long maxSize,
long expireAfterWrite) {
this.url = jdbcUrl; this.url = jdbcUrl;
this.keyType = KeyType.create(keyType); this.keyType = KeyType.create(keyType);
this.maxSize = maxSize; this.maxSize = maxSize;
this.expireAfterWrite = expireAfterWrite;
int cores = Runtime.getRuntime().availableProcessors(); int cores = Runtime.getRuntime().availableProcessors();
int keep = Math.min(cores, 16); int keep = Math.min(cores, 16);
@@ -408,7 +411,7 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> implements
try { try {
c = acquire(); c = acquire();
if (c.get == null) { if (c.get == null) {
c.get = c.conn.prepareStatement("SELECT v FROM data WHERE k=?"); c.get = c.conn.prepareStatement("SELECT v, created FROM data WHERE k=?");
} }
keyType.set(c.get, 1, key); keyType.set(c.get, 1, key);
ResultSet r = c.get.executeQuery(); ResultSet r = c.get.executeQuery();
@@ -418,6 +421,13 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> implements
return null; return null;
} }
Timestamp created = r.getTimestamp(2);
if (expired(created)) {
invalidate(key);
missCount.incrementAndGet();
return null;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
V val = (V) r.getObject(1); V val = (V) r.getObject(1);
ValueHolder<V> h = new ValueHolder<>(val); ValueHolder<V> h = new ValueHolder<>(val);
@@ -438,6 +448,14 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> implements
} }
} }
private boolean expired(Timestamp created) {
if (expireAfterWrite == 0) {
return false;
}
long age = TimeUtil.nowMs() - created.getTime();
return 1000 * expireAfterWrite < age;
}
private void touch(SqlHandle c, K key) throws SQLException { private void touch(SqlHandle c, K key) throws SQLException {
if (c.touch == null) { if (c.touch == null) {
c.touch =c.conn.prepareStatement("UPDATE data SET accessed=? WHERE k=?"); c.touch =c.conn.prepareStatement("UPDATE data SET accessed=? WHERE k=?");
@@ -552,12 +570,14 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> implements
r = s.executeQuery("SELECT" r = s.executeQuery("SELECT"
+ " k" + " k"
+ ",OCTET_LENGTH(k) + OCTET_LENGTH(v)" + ",OCTET_LENGTH(k) + OCTET_LENGTH(v)"
+ ",created"
+ " FROM data" + " FROM data"
+ " ORDER BY accessed"); + " ORDER BY accessed");
try { try {
while (maxSize < used && r.next()) { while (maxSize < used && r.next()) {
K key = keyType.get(r, 1); K key = keyType.get(r, 1);
if (mem.getIfPresent(key) != null) { Timestamp created = r.getTimestamp(3);
if (mem.getIfPresent(key) != null && !expired(created)) {
touch(c, key); touch(c, key);
} else { } else {
invalidate(c, key); invalidate(c, key);