[
https://issues.apache.org/jira/browse/OAK-3168?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14647676#comment-14647676
]
Thomas Mueller commented on OAK-3168:
-------------------------------------
Possibly patch (just for the cache; no tests yet, and SegmentCache not yet
changed):
{noformat}
--- src/main/java/org/apache/jackrabbit/oak/cache/CacheLIRS.java
(revision 1692465)
+++ src/main/java/org/apache/jackrabbit/oak/cache/CacheLIRS.java
(working copy)
@@ -34,6 +34,7 @@
import com.google.common.cache.CacheLoader;
import com.google.common.cache.CacheStats;
import com.google.common.cache.LoadingCache;
+import com.google.common.cache.RemovalCause;
import com.google.common.cache.Weigher;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ListenableFuture;
@@ -93,10 +94,11 @@
* The method may be called twice for the same key (first if the entry
* is resident, and later if the entry is non-resident).
*
+ * @param cause the removal cause
* @param key the evicted item's key
* @param value the evicted item's value or {@code null} if
non-resident
*/
- void evicted(@Nonnull K key, @Nullable V value);
+ void evicted(RemovalCause cause, @Nonnull K key, @Nullable V value);
}
/**
@@ -205,13 +207,13 @@
}
}
- void evicted(Entry<K, V> entry) {
+ void evicted(RemovalCause cause, Entry<K, V> entry) {
if (evicted == null) {
return;
}
K key = entry.key;
if (key != null) {
- evicted.evicted(key, entry.value);
+ evicted.evicted(cause, key, entry.value);
}
}
@@ -300,9 +302,18 @@
* @throws ExecutionException
*/
@Override
- public V get(K key) throws ExecutionException {
+ public V get(final K key) throws ExecutionException {
int hash = getHash(key);
- return getSegment(hash).get(key, hash, loader);
+ if (loader != null) {
+ return getSegment(hash).get(key, hash,
+ new Callable<V>() {
+ @Override
+ public V call() throws Exception {
+ return loader.load(key);
+ }
+ });
+ }
+ return getSegment(hash).get(key, hash);
}
/**
@@ -383,7 +394,7 @@
@Override
public void invalidate(Object key) {
int hash = getHash(key);
- getSegment(hash).invalidate(key, hash);
+ getSegment(hash).invalidate(RemovalCause.EXPLICIT, key, hash);
}
/**
@@ -777,16 +788,16 @@
public void evictedAll() {
for (Entry<K, V> e = stack.stackNext; e != stack; e = e.stackNext)
{
if (e.value != null) {
- cache.evicted(e);
+ cache.evicted(RemovalCause.EXPLICIT, e);
}
}
for (Entry<K, V> e = queue.queueNext; e != queue; e = e.queueNext)
{
if (e.stackNext == null) {
- cache.evicted(e);
+ cache.evicted(RemovalCause.EXPLICIT, e);
}
}
for (Entry<K, V> e = queue2.queueNext; e != queue2; e =
e.queueNext) {
- cache.evicted(e);
+ cache.evicted(RemovalCause.EXPLICIT, e);
}
}
@@ -993,36 +1004,6 @@
return value;
}
- V get(K key, int hash, CacheLoader<K, V> loader) throws
ExecutionException {
- // avoid synchronization if it's in the cache
- V value = get(key, hash);
- if (value != null) {
- return value;
- }
- if (loader == null) {
- return null;
- }
- synchronized (this) {
- value = get(key, hash);
- if (value != null) {
- return value;
- }
- long start = System.nanoTime();
- try {
- value = loader.load(key);
- loadSuccessCount++;
- } catch (Exception e) {
- loadExceptionCount++;
- throw new ExecutionException(e);
- } finally {
- long time = System.nanoTime() - start;
- totalLoadTime += time;
- }
- put(key, hash, value, cache.sizeOf(key, value));
- return value;
- }
- }
-
synchronized V replace(K key, int hash, V value, int memory) {
if (containsKey(key, hash)) {
return put(key, hash, value, memory);
@@ -1042,7 +1023,7 @@
synchronized boolean remove(Object key, int hash, Object value) {
V old = get(key, hash);
if (old != null && old.equals(value)) {
- invalidate(key, hash);
+ invalidate(RemovalCause.EXPLICIT, key, hash);
return true;
}
return false;
@@ -1051,7 +1032,7 @@
synchronized V remove(Object key, int hash) {
V old = get(key, hash);
// even if old is null, there might still be a cold entry
- invalidate(key, hash);
+ invalidate(RemovalCause.EXPLICIT, key, hash);
return old;
}
@@ -1111,7 +1092,7 @@
old = null;
} else {
old = e.value;
- invalidate(key, hash);
+ invalidate(RemovalCause.REPLACED, key, hash);
}
e = new Entry<K, V>();
e.key = key;
@@ -1137,10 +1118,11 @@
* Remove an entry. Both resident and non-resident entries can be
* removed.
*
+ * @param cause the removal cause
* @param key the key (may not be null)
* @param hash the hash
*/
- synchronized void invalidate(Object key, int hash) {
+ synchronized void invalidate(RemovalCause cause, Object key, int hash)
{
Entry<K, V>[] array = entries;
int mask = array.length - 1;
int index = hash & mask;
@@ -1180,7 +1162,7 @@
removeFromQueue(e);
}
pruneStack();
- cache.evicted(e);
+ cache.evicted(cause, e);
}
/**
@@ -1208,7 +1190,7 @@
usedMemory -= e.memory;
evictionCount++;
removeFromQueue(e);
- cache.evicted(e);
+ cache.evicted(RemovalCause.SIZE, e);
e.value = null;
e.memory = 0;
addToQueue(queue2, e);
@@ -1216,7 +1198,7 @@
while (queue2Size + queue2Size > stackSize) {
e = queue2.queuePrev;
int hash = getHash(e.key);
- invalidate(e.key, hash);
+ invalidate(RemovalCause.SIZE, e.key, hash);
}
}
}
{noformat}
> SegmentCache flushes Segment on update
> --------------------------------------
>
> Key: OAK-3168
> URL: https://issues.apache.org/jira/browse/OAK-3168
> Project: Jackrabbit Oak
> Issue Type: Bug
> Components: segmentmk
> Reporter: Alex Parvulescu
> Assignee: Alex Parvulescu
> Fix For: 1.3.5
>
>
> The SegmentCache currently uses the cache eviction call to remove the Segment
> instance from memory to help keep the cache memory requirements under control
> [0].
> What I've noticed though, is that for a cache update (existing key) there
> will also be an eviction call happening, which results in a lot of extra IO
> pressure on the SegmentStore which not only is not able to cache the segment,
> but is forced to reload it multiple times as the reference gets nullified
> after each load.
> This comes from the sampling behavior of the SegmentId: it will not hit the
> cache each time it needs to load a new Segment, but rather load it from IO
> and (re)place it in the cache, based on a sampling rate [1].
> Now I see 2 options:
> * change the cache code to _not_ call the eviction callback on updates (or
> allow disabling this call on updates)
> * change the SegmentTracker code to add the value to the cache only if it's
> not there as Segments are immutable, so no harm done.
> Raised this issue offline with [~tmueller], [~mduerig] first and as I
> understand [~mduerig] is in favor of option one, while [~tmueller] proposed
> that the Lirs cache impl should be inline with what the guava cache does, and
> depending on that we could choose the right fix here.
> Hope this covers everything.
> [0]
> https://github.com/apache/jackrabbit-oak/blob/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentTracker.java#L133
> [1]
> https://github.com/apache/jackrabbit-oak/blob/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentId.java#L135
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)