You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by md...@apache.org on 2016/09/12 14:06:43 UTC
svn commit: r1760372 - in /jackrabbit/oak/trunk/oak-segment-tar/src:
main/java/org/apache/jackrabbit/oak/segment/
main/java/org/apache/jackrabbit/oak/segment/file/
test/java/org/apache/jackrabbit/oak/segment/
test/java/org/apache/jackrabbit/oak/segment...
Author: mduerig
Date: Mon Sep 12 14:06:43 2016
New Revision: 1760372
URL: http://svn.apache.org/viewvc?rev=1760372&view=rev
Log:
OAK-4635: Improve cache eviction policy of the node deduplication cache
Replace the level based cache with a cache where mappings can have an costs associated. Use the number of child nodes of a node as cost.
Added:
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/PriorityCache.java
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/PriorityCacheTest.java
Removed:
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/NodeCacheTest.java
Modified:
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/NodeCache.java
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentWriter.java
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/WriterCacheManager.java
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreBuilder.java
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/NodeRecordTest.java
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/WriteCacheManagerTest.java
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java
jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/benchmark/BenchmarkBase.java
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/NodeCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/NodeCache.java?rev=1760372&r1=1760371&r2=1760372&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/NodeCache.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/NodeCache.java Mon Sep 12 14:06:43 2016
@@ -19,200 +19,24 @@
package org.apache.jackrabbit.oak.segment;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Sets.newHashSet;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
-import com.google.common.base.Supplier;
-import com.google.common.cache.CacheStats;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
/**
- * Partial mapping of string keys to values of type {@link RecordId}. This is
- * typically used for de-duplicating nodes that have already been persisted and thus
- * already have a {@code RecordId}.
+ * Partial mapping of {@code String} keys to values of type {@link RecordId}. The mappings
+ * can further be associated with a cost, which is a metric for the cost occurring when the
+ * given mapping is lost. Higher values represent higher costs.
*/
-public abstract class NodeCache {
- private long hitCount;
- private long missCount;
- private long loadCount;
- private long evictionCount;
+public interface NodeCache {
/**
- * Add a mapping from {@code key} to {@code value} where {@code value} is the
- * id of a node at the given {@code depth}. Any existing mapping is replaced.
+ * Add a mapping from {@code key} to {@code value} with a given {@code cost}.
*/
- public abstract void put(@Nonnull String key, @Nonnull RecordId value, int depth);
+ void put(@Nonnull String stableId, @Nonnull RecordId recordId, byte cost);
/**
* @return The mapping for {@code key}, or {@code null} if none.
*/
@CheckForNull
- public abstract RecordId get(@Nonnull String key);
-
- /**
- * @return number of mappings
- */
- public abstract long size();
-
- /**
- * @return access statistics for this cache
- */
- @Nonnull
- public CacheStats getStats() {
- return new CacheStats(hitCount, missCount, loadCount, 0, 0, evictionCount);
- }
-
- /**
- * Factory method for creating {@code NodeCache} instances with a given
- * {@code capacity} and maximal depth. The returned instances are all
- * thread safe.
- *
- * Mappings with a depth exceeding {@code maxDepth} will not be added.
- *
- * If the number of mappings exceed {@code capacity} the maximal depth
- * is decreased and all mappings exceeding that new value are removed.
- *
- * @param capacity maximal number of mappings
- * @param maxDepth maximal depth
- * @return A new {@code RecordCache} instance of the given {@code size}.
- */
-
- @Nonnull
- public static NodeCache newNodeCache(int capacity, int maxDepth) {
- if (capacity <= 0) {
- return new NodeCache.Empty();
- } else {
- return new NodeCache.Default(capacity, maxDepth);
- }
- }
-
- /**
- * @return A factory returning {@code Node} instances of the given
- * {@code capacity} and {@code maxDepth} when invoked.
- * @see #newNodeCache(int, int)
- */
- @Nonnull
- public static Supplier<NodeCache> factory(int capacity, int maxDepth) {
- if (capacity <= 0) {
- return NodeCache.Empty.supplier();
- } else {
- return NodeCache.Default.supplier(capacity, maxDepth);
- }
- }
-
- private static class Empty extends NodeCache {
- static final Supplier<NodeCache> supplier() {
- return new Supplier<NodeCache>() {
- @Override
- public NodeCache get() {
- return new NodeCache.Empty();
- }
- };
- }
-
- @Override
- public synchronized void put(@Nonnull String key, @Nonnull RecordId value, int depth) { }
-
- @Override
- public synchronized RecordId get(@Nonnull String key) {
- super.missCount++;
- return null;
- }
-
- @Override
- public long size() {
- return 0;
- }
- }
-
- private static class Default extends NodeCache {
- private static final Logger LOG = LoggerFactory.getLogger(Default.class);
-
- private final int capacity;
- private final List<Map<String, RecordId>> caches;
-
- private int size;
-
- private final Set<Integer> muteDepths = newHashSet();
-
- static final Supplier<NodeCache> supplier(final int capacity, final int size) {
- return new Supplier<NodeCache>() {
- @Override
- public NodeCache get() {
- return new NodeCache.Default(capacity, size);
- }
- };
- }
-
- Default(int capacity, int maxDepth) {
- checkArgument(capacity > 0);
- checkArgument(maxDepth > 0);
- this.capacity = capacity;
- this.caches = newArrayList();
- for (int k = 0; k < maxDepth; k++) {
- caches.add(new HashMap<String, RecordId>());
- }
- }
-
- @Override
- public synchronized void put(@Nonnull String key, @Nonnull RecordId value, int depth) {
- while (size >= capacity) {
- int d = caches.size() - 1;
- int removed = caches.remove(d).size();
- size -= removed;
- super.evictionCount -= removed;
- if (removed > 0) {
- LOG.info("Evicted cache at depth {} as size {} reached capacity {}. " +
- "New size is {}", d, size + removed, capacity, size);
- }
- }
-
- if (depth < caches.size()) {
- if (caches.get(depth).put(key, value) == null) {
- super.loadCount++;
- size++;
- }
- } else {
- if (muteDepths.add(depth)) {
- LOG.info("Not caching {} -> {} as depth {} reaches or exceeds the maximum of {}",
- key, value, depth, caches.size());
- }
- }
- }
-
- @Override
- public synchronized RecordId get(@Nonnull String key) {
- for (Map<String, RecordId> cache : caches) {
- if (!cache.isEmpty()) {
- RecordId recordId = cache.get(key);
- if (recordId != null) {
- super.hitCount++;
- return recordId;
- }
- }
- }
- super.missCount++;
- return null;
- }
-
- @Override
- public synchronized long size() {
- long size = 0;
- for (Map<String, RecordId> cache : caches) {
- size += cache.size();
- }
- return size;
- }
- }
-
+ RecordId get(@Nonnull String stableId);
}
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java?rev=1760372&r1=1760371&r2=1760372&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java Mon Sep 12 14:06:43 2016
@@ -176,20 +176,14 @@ public class SegmentNodeStoreService ext
public static final String TEMPLATE_DEDUPLICATION_CACHE_SIZE = "templateDeduplicationCache.size";
@Property(
- intValue = 10000000,
- label = "Node deduplication cache size (#items)",
- description = "Maximum number of nodes to keep in the deduplication cache"
+ intValue = 8388608,
+ label = "Node deduplication cache size (#items)",
+ description = "Maximum number of node to keep in the deduplication cache. If the supplied" +
+ " value is not a power of 2, it will be rounded up to the next power of 2."
)
public static final String NODE_DEDUPLICATION_CACHE_SIZE = "nodeDeduplicationCache.size";
@Property(
- intValue = 20,
- label = "Node deduplication cache depth (#levels)",
- description = "Maximum number of levels to keep in the node deduplication cache"
- )
- public static final String NODE_DEDUPLICATION_CACHE_DEPTH = "nodeDeduplicationCache.depth";
-
- @Property(
byteValue = MEMORY_THRESHOLD_DEFAULT,
label = "Memory Multiplier",
description = "TarMK compaction available memory multiplier needed to run compaction"
@@ -396,7 +390,6 @@ public class SegmentNodeStoreService ext
.withStringDeduplicationCacheSize(getStringDeduplicationCacheSize())
.withTemplateDeduplicationCacheSize(getTemplateDeduplicationCacheSize())
.withNodeDeduplicationCacheSize(getNodeDeduplicationCacheSize())
- .withNodeDeduplicationDepth(getNodeDeduplicationDepth())
.withMaxFileSize(getMaxFileSize())
.withMemoryMapping(getMode().equals("64"))
.withGCMonitor(gcMonitor)
@@ -710,11 +703,9 @@ public class SegmentNodeStoreService ext
}
private int getNodeDeduplicationCacheSize() {
- return Integer.parseInt(getCacheSize(NODE_DEDUPLICATION_CACHE_SIZE));
- }
-
- private int getNodeDeduplicationDepth() {
- return Integer.parseInt(getCacheSize(NODE_DEDUPLICATION_CACHE_DEPTH));
+ // Round to the next power of 2
+ int size = Math.max(1, Integer.parseInt(getCacheSize(NODE_DEDUPLICATION_CACHE_SIZE)));
+ return 1 << (32 - Integer.numberOfLeadingZeros(size - 1));
}
private String getMaxFileSizeProperty() {
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentWriter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentWriter.java?rev=1760372&r1=1760371&r2=1760372&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentWriter.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentWriter.java Mon Sep 12 14:06:43 2016
@@ -33,6 +33,8 @@ import static com.google.common.collect.
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.io.ByteStreams.read;
import static java.lang.Integer.getInteger;
+import static java.lang.Long.numberOfLeadingZeros;
+import static java.lang.Math.min;
import static java.lang.System.nanoTime;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
@@ -160,6 +162,16 @@ public class SegmentWriter {
}
/**
+ * Get occupancy information for the node deduplication cache indicating occupancy and
+ * evictions per priority.
+ * @return occupancy information for the node deduplication cache.
+ */
+ @CheckForNull
+ public String getNodeCacheOccupancyInfo() {
+ return cacheManager.getNodeCacheOccupancyInfo();
+ }
+
+ /**
* @return Statistics for node write times (in ns).
* The statistics are collected with a window size defined by {@link #NODE_WRITER_STATS_WINDOW}
* @see #getNodeCompactTimeStats()
@@ -727,7 +739,7 @@ public class SegmentWriter {
// inline the remaining data as block records
while (pos < data.length) {
- int len = Math.min(BLOCK_SIZE, data.length - pos);
+ int len = min(BLOCK_SIZE, data.length - pos);
blockIds.add(writeBlock(data, pos, len));
pos += len;
}
@@ -988,9 +1000,10 @@ public class SegmentWriter {
} finally {
if (nodeWriteStats.isCompactOp) {
nodeCompactTimeStats.addValue(nanoTime() - nodeWriteStats.startTime);
- LOG.info(nodeWriteStats.toString());
+ LOG.info("{}", nodeWriteStats);
} else {
nodeWriteTimeStats.addValue(nanoTime() - nodeWriteStats.startTime);
+ LOG.debug("{}", nodeWriteStats);
}
}
}
@@ -1016,12 +1029,17 @@ public class SegmentWriter {
// generation (e.g. due to compaction). Put it into the cache for
// deduplication of hard links to it (e.g. checkpoints).
SegmentNodeState sns = (SegmentNodeState) state;
- nodeCache.put(sns.getStableId(), recordId, depth);
+ nodeCache.put(sns.getStableId(), recordId, cost(sns));
nodeWriteStats.isCompactOp = true;
}
return recordId;
}
+ private byte cost(SegmentNodeState node) {
+ long childCount = node.getChildNodeCount(Long.MAX_VALUE);
+ return (byte) (Byte.MIN_VALUE + 64 - numberOfLeadingZeros(childCount));
+ }
+
private RecordId writeNodeUncached(@Nonnull NodeState state, int depth) throws IOException {
ModifiedNodeState after = null;
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/WriterCacheManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/WriterCacheManager.java?rev=1760372&r1=1760371&r2=1760372&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/WriterCacheManager.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/WriterCacheManager.java Mon Sep 12 14:06:43 2016
@@ -38,6 +38,7 @@ import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheStats;
import org.apache.jackrabbit.oak.api.jmx.CacheStatsMBean;
+import org.apache.jackrabbit.oak.segment.file.PriorityCache;
/**
* Instances of this class manage the deduplication caches used
@@ -63,18 +64,11 @@ public abstract class WriterCacheManager
"oak.tar.templatesCacheSize", 3000);
/**
- * Default capacity of the node cache.
+ * Default size of the node deduplication cache.
* @see #getNodeCache(int)
*/
- public static final int DEFAULT_NODE_CACHE_CAPACITY = getInteger(
- "oak.tar.nodeCacheCapacity", 1000000);
-
- /**
- * Default maximal depth of the node cache.
- * @see #getNodeCache(int)
- */
- public static final int DEFAULT_NODE_CACHE_DEPTH = getInteger(
- "oak.tar.nodeCacheDepth", 20);
+ public static final int DEFAULT_NODE_CACHE_SIZE = getInteger(
+ "oak.tar.nodeCacheSize", 8388608);
/**
* @param generation
@@ -122,6 +116,14 @@ public abstract class WriterCacheManager
}
/**
+ * Get occupancy information for the node deduplication cache indicating occupancy and
+ * evictions per priority.
+ * @return occupancy information for the node deduplication cache.
+ */
+ @CheckForNull
+ public String getNodeCacheOccupancyInfo() { return null; }
+
+ /**
* This implementation of {@link WriterCacheManager} returns empty caches
* of size 0.
* @see #INSTANCE
@@ -135,7 +137,6 @@ public abstract class WriterCacheManager
private final RecordCache<String> stringCache = newRecordCache(0);
private final RecordCache<Template> templateCache = newRecordCache(0);
- private final NodeCache nodeCache = NodeCache.newNodeCache(0, 0);
private Empty() {}
@@ -156,18 +157,26 @@ public abstract class WriterCacheManager
}
/**
- * @return empty cache of size 0
+ * @return a {@code NodeCache} cache that is always empty
*/
+ @Nonnull
@Override
public NodeCache getNodeCache(int generation) {
- return nodeCache;
+ return new NodeCache() {
+ @Override
+ public void put(@Nonnull String stableId, @Nonnull RecordId recordId, byte cost) { }
+
+ @CheckForNull
+ @Override
+ public RecordId get(@Nonnull String stableId) { return null; }
+ };
}
}
/**
* This implementation of {@link WriterCacheManager} returns
* {@link RecordCache} instances for the string and template cache
- * and {@link NodeCache} instances for the node cache.
+ * and {@link NodeCache} instance for the node cache.
*/
public static class Default extends WriterCacheManager {
/**
@@ -186,7 +195,7 @@ public abstract class WriterCacheManager
* Cache of recently stored nodes to avoid duplicating linked nodes (i.e. checkpoints)
* during compaction.
*/
- private final Generations<NodeCache> nodeCaches;
+ private final Supplier<PriorityCache<String, RecordId>> nodeCache;
/**
* New instance using the passed factories for creating cache instances.
@@ -200,21 +209,22 @@ public abstract class WriterCacheManager
public Default(
@Nonnull Supplier<RecordCache<String>> stringCacheFactory,
@Nonnull Supplier<RecordCache<Template>> templateCacheFactory,
- @Nonnull Supplier<NodeCache> nodeCacheFactory) {
+ @Nonnull Supplier<PriorityCache<String, RecordId>> nodeCacheFactory) {
this.stringCaches = new Generations<>(stringCacheFactory);
this.templateCaches = new Generations<>(templateCacheFactory);
- this.nodeCaches = new Generations<>(nodeCacheFactory);
+ this.nodeCache = memoize(nodeCacheFactory);
}
/**
* New instance using the default factories {@link RecordCache#factory(int)}
- * and {@link NodeCache#factory(int, int)} with the sizes
- * {@link #DEFAULT_STRING_CACHE_SIZE} and {@link #DEFAULT_TEMPLATE_CACHE_SIZE}.
+ * and {@link PriorityCache#factory(int)} with the sizes
+ * {@link #DEFAULT_STRING_CACHE_SIZE}, {@link #DEFAULT_TEMPLATE_CACHE_SIZE}
+ * and {@link #DEFAULT_NODE_CACHE_SIZE}.
*/
public Default() {
this(RecordCache.<String>factory(DEFAULT_STRING_CACHE_SIZE),
RecordCache.<Template>factory(DEFAULT_TEMPLATE_CACHE_SIZE),
- NodeCache.factory(DEFAULT_NODE_CACHE_CAPACITY, DEFAULT_NODE_CACHE_DEPTH));
+ PriorityCache.<String, RecordId>factory(DEFAULT_NODE_CACHE_SIZE));
}
private static class Generations<T> implements Iterable<T> {
@@ -265,10 +275,21 @@ public abstract class WriterCacheManager
return templateCaches.getGeneration(generation);
}
- @Nonnull
@Override
- public NodeCache getNodeCache(int generation) {
- return nodeCaches.getGeneration(generation);
+ @Nonnull
+ public NodeCache getNodeCache(final int generation) {
+ return new NodeCache() {
+ @Override
+ public void put(@Nonnull String stableId, @Nonnull RecordId recordId, byte cost) {
+ nodeCache.get().put(stableId, recordId, generation, cost);
+ }
+
+ @CheckForNull
+ @Override
+ public RecordId get(@Nonnull String stableId) {
+ return nodeCache.get().get(stableId, generation);
+ }
+ };
}
@CheckForNull
@@ -319,37 +340,23 @@ public abstract class WriterCacheManager
@Override
public CacheStatsMBean getNodeCacheStats() {
return new RecordCacheStats("Node deduplication cache stats",
- accumulateNodeCacheStats(nodeCaches), accumulateNodeCacheSizes(nodeCaches));
+ new Supplier<CacheStats>() {
+ @Override
+ public CacheStats get() {
+ return nodeCache.get().getStats();
+ }
+ },
+ new Supplier<Long>() {
+ @Override
+ public Long get() {
+ return nodeCache.get().size();
+ }
+ });
}
- @Nonnull
- private static <T> Supplier<CacheStats> accumulateNodeCacheStats(
- final Iterable<NodeCache> caches) {
- return new Supplier<CacheStats>() {
- @Override
- public CacheStats get() {
- CacheStats stats = new CacheStats(0, 0, 0, 0, 0, 0);
- for (NodeCache cache : caches) {
- stats = stats.plus(cache.getStats());
- }
- return stats;
- }
- };
- }
-
- @Nonnull
- public static <T> Supplier<Long> accumulateNodeCacheSizes(
- final Iterable<NodeCache> caches) {
- return new Supplier<Long>() {
- @Override
- public Long get() {
- long size = 0;
- for (NodeCache cache : caches) {
- size += cache.size();
- }
- return size;
- }
- };
+ @Override
+ public String getNodeCacheOccupancyInfo() {
+ return nodeCache.get().toString();
}
/**
@@ -359,7 +366,6 @@ public abstract class WriterCacheManager
protected final void evictCaches(Predicate<Integer> generations) {
stringCaches.evictGenerations(generations);
templateCaches.evictGenerations(generations);
- nodeCaches.evictGenerations(generations);
}
}
}
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java?rev=1760372&r1=1760371&r2=1760372&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java Mon Sep 12 14:06:43 2016
@@ -534,10 +534,12 @@ public class FileStore implements Segmen
if (sufficientEstimatedGain) {
if (!gcOptions.isPaused()) {
logAndClear(segmentWriter.getNodeWriteTimeStats(), segmentWriter.getNodeCompactTimeStats());
+ log(segmentWriter.getNodeCacheOccupancyInfo());
if (compact()) {
cleanupNeeded.set(cleanup);
}
logAndClear(segmentWriter.getNodeWriteTimeStats(), segmentWriter.getNodeCompactTimeStats());
+ log(segmentWriter.getNodeCacheOccupancyInfo());
} else {
gcListener.skipped("TarMK GC #{}: compaction paused", GC_COUNT);
}
@@ -553,6 +555,12 @@ public class FileStore implements Segmen
nodeCompactTimeStats.clear();
}
+ private static void log(@CheckForNull String nodeCacheOccupancyInfo) {
+ if (nodeCacheOccupancyInfo != null) {
+ log.info("NodeCache occupancy: {}", nodeCacheOccupancyInfo);
+ }
+ }
+
private static String toString(DescriptiveStatistics statistics) {
DecimalFormat sci = new DecimalFormat("##0.0E0");
DecimalFormatSymbols symbols = sci.getDecimalFormatSymbols();
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreBuilder.java?rev=1760372&r1=1760371&r2=1760372&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreBuilder.java Mon Sep 12 14:06:43 2016
@@ -24,8 +24,7 @@ import static com.google.common.base.Pre
import static org.apache.jackrabbit.oak.segment.CachingSegmentReader.DEFAULT_STRING_CACHE_MB;
import static org.apache.jackrabbit.oak.segment.CachingSegmentReader.DEFAULT_TEMPLATE_CACHE_MB;
import static org.apache.jackrabbit.oak.segment.SegmentCache.DEFAULT_SEGMENT_CACHE_MB;
-import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_NODE_CACHE_CAPACITY;
-import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_NODE_CACHE_DEPTH;
+import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_NODE_CACHE_SIZE;
import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_STRING_CACHE_SIZE;
import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_TEMPLATE_CACHE_SIZE;
import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.defaultGCOptions;
@@ -37,8 +36,8 @@ import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import com.google.common.base.Predicate;
-import org.apache.jackrabbit.oak.segment.NodeCache;
import org.apache.jackrabbit.oak.segment.RecordCache;
+import org.apache.jackrabbit.oak.segment.RecordId;
import org.apache.jackrabbit.oak.segment.Template;
import org.apache.jackrabbit.oak.segment.WriterCacheManager;
import org.apache.jackrabbit.oak.segment.compaction.LoggingGCMonitor;
@@ -75,9 +74,7 @@ public class FileStoreBuilder {
private int templateDeduplicationCacheSize = DEFAULT_TEMPLATE_CACHE_SIZE;
- private int nodeDeduplicationCacheSize = DEFAULT_NODE_CACHE_CAPACITY;
-
- private int nodeDeduplicationCacheDepth = DEFAULT_NODE_CACHE_DEPTH;
+ private int nodeDeduplicationCacheSize = DEFAULT_NODE_CACHE_SIZE;
private boolean memoryMapping = FileStore.MEMORY_MAPPING_DEFAULT;
@@ -241,7 +238,7 @@ public class FileStoreBuilder {
/**
* Number of items to keep in the node deduplication cache
- * @param nodeDeduplicationCacheSize None negative cache size
+ * @param nodeDeduplicationCacheSize None negative cache size. Must be a power of 2.
* @return this instance
*/
@Nonnull
@@ -251,17 +248,6 @@ public class FileStoreBuilder {
}
/**
- * Maximal depth of the node deduplication cache
- * @param nodeDeduplicationCacheDepth
- * @return this instance
- */
- @Nonnull
- public FileStoreBuilder withNodeDeduplicationDepth(int nodeDeduplicationCacheDepth) {
- this.nodeDeduplicationCacheDepth = nodeDeduplicationCacheDepth;
- return this;
- }
-
- /**
* Turn memory mapping on or off
* @param memoryMapping
* @return this instance
@@ -422,8 +408,7 @@ public class FileStoreBuilder {
WriterCacheManager getCacheManager() {
if (cacheManager == null) {
cacheManager = new EvictingWriteCacheManager(
- stringDeduplicationCacheSize, templateDeduplicationCacheSize,
- nodeDeduplicationCacheSize, nodeDeduplicationCacheDepth);
+ stringDeduplicationCacheSize, templateDeduplicationCacheSize, nodeDeduplicationCacheSize);
}
return cacheManager;
}
@@ -440,18 +425,16 @@ public class FileStoreBuilder {
", stringDeduplicationCacheSize=" + stringDeduplicationCacheSize +
", templateDeduplicationCacheSize=" + templateDeduplicationCacheSize +
", nodeDeduplicationCacheSize=" + nodeDeduplicationCacheSize +
- ", nodeDeduplicationCacheDepth=" + nodeDeduplicationCacheDepth +
", memoryMapping=" + memoryMapping +
", gcOptions=" + gcOptions +
'}';
}
private static class EvictingWriteCacheManager extends WriterCacheManager.Default {
- public EvictingWriteCacheManager(int stringCacheSize, int templateCacheSize,
- int nodeCacheCapacity, int nodeCacheDepth) {
+ public EvictingWriteCacheManager(int stringCacheSize, int templateCacheSize, int nodeCacheSize) {
super(RecordCache.<String>factory(stringCacheSize),
RecordCache.<Template>factory(templateCacheSize),
- NodeCache.factory(nodeCacheCapacity, nodeCacheDepth));
+ PriorityCache.<String, RecordId>factory(nodeCacheSize));
}
void evictOldGeneration(final int newGeneration) {
Added: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/PriorityCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/PriorityCache.java?rev=1760372&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/PriorityCache.java (added)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/PriorityCache.java Mon Sep 12 14:06:43 2016
@@ -0,0 +1,254 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.oak.segment.file;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.Integer.bitCount;
+import static java.lang.Integer.numberOfTrailingZeros;
+import static java.util.Arrays.fill;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import com.google.common.base.Supplier;
+import com.google.common.cache.CacheStats;
+
+/**
+ * {@code PriorityCache} implements a partial mapping from keys of type {@code K} to values
+ * of type {@code V}. Mappings are associates with a cost, which states how expensive it is
+ * to recreate that mapping. This cache uses the cost such that mappings with a higher cost
+ * have a lower chance of being evicted than mappings with a lower cost. When an item from
+ * this cache is successfully looked up its cost is incremented by one, unless it has reached
+ * its maximum cost of {@link Byte#MAX_VALUE} already.
+ * <p>
+ * Additionally this cache tracks a generation for mappings. Mappings of later generations
+ * always take precedence over mappings of earlier generations. That is, putting a mapping of
+ * a later generation into the cache can cause any mapping of an earlier generation to be evicted
+ * regardless of its cost.
+ * <p>
+ * This cache uses rehashing to resolve clashes. The number of rehashes is configurable. When
+ * a clash cannot be resolved by rehashing the given number of times the put operation fails.
+ * <p>
+ * This cache is thread safe.
+ * @param <K> type of the keys
+ * @param <V> type of the values
+ */
+public class PriorityCache<K, V> {
+ private final int rehash;
+ private final Entry<?,?>[] entries;
+ private final int[] costs = new int[256];
+ private final int[] evictions = new int[256];
+
+ private long hitCount;
+ private long missCount;
+ private long loadCount;
+ private long evictionCount;
+ private long size;
+
+ /**
+ * Static factory for creating new {@code PriorityCache} instances.
+ * @param size size of the cache. Must be a power of 2.
+ * @return a new {@code PriorityCache} instance of the given {@code size}.
+ */
+ public static <K, V> Supplier<PriorityCache<K, V>> factory(final int size) {
+ checkArgument(bitCount(size) == 1);
+ return new Supplier<PriorityCache<K, V>>() {
+ @Override
+ public PriorityCache<K, V> get() {
+ return new PriorityCache<>(size);
+ }
+ };
+ }
+
+ private static class Entry<K, V> {
+ static Entry<Void, Void> NULL = new Entry<>(null, null, -1, Byte.MIN_VALUE);
+
+ final K key;
+ final V value;
+ final int generation;
+ byte cost;
+
+ public Entry(K key, V value, int generation, byte cost) {
+ this.key = key;
+ this.value = value;
+ this.generation = generation;
+ this.cost = cost;
+ }
+
+ @Override
+ public String toString() {
+ return this == NULL
+ ? "NULL"
+ : "Entry{" + key + "->" + value + " @" + generation + ", $" + cost + "}";
+ }
+ }
+
+ /**
+ * Create a new instance of the given {@code size}. {@code rehash} specifies the number
+ * of rehashes to resolve a clash.
+ * @param size Size of the cache. Must be a power of {@code 2}.
+ * @param rehash Number of rehashes. Must be greater or equal to {@code 0} and
+ * smaller than {@code 32 - numberOfTrailingZeros(size)}.
+ */
+ public PriorityCache(int size, int rehash) {
+ checkArgument(bitCount(size) == 1);
+ checkArgument(rehash >= 0);
+ checkArgument(rehash < 32 - numberOfTrailingZeros(size));
+ this.rehash = rehash;
+ entries = new Entry<?,?>[size];
+ fill(entries, Entry.NULL);
+ }
+
+ /**
+ * Create a new instance of the given {@code size}. The number of rehashes is
+ * the maximum number allowed by the given {@code size}. ({@code 31 - numberOfTrailingZeros(size)}.
+ * @param size Size of the cache. Must be a power of {@code 2}.
+ */
+ public PriorityCache(int size) {
+ this(size, 31 - numberOfTrailingZeros(size));
+ }
+
+ private int project(int hashCode, int iteration) {
+ return (hashCode >> iteration) & (entries.length - 1);
+ }
+
+ /**
+ * @return the number of mappings in this cache.
+ */
+ public long size() {
+ return size;
+ }
+
+ /**
+ * Add a mapping to the cache.
+ * @param key the key of the mapping
+ * @param value the value of the mapping
+ * @param generation the generation of the mapping
+ * @param initialCost the initial cost associated with this mapping
+ * @return {@code true} if the mapping has been added, {@code false} otherwise.
+ */
+ public synchronized boolean put(@Nonnull K key, @Nonnull V value, int generation, byte initialCost) {
+ int hashCode = key.hashCode();
+ byte cheapest = initialCost;
+ int index = -1;
+ boolean eviction = false;
+ for (int k = 0; k <= rehash; k++) {
+ int i = project(hashCode, k);
+ Entry<?, ?> entry = entries[i];
+ if (entry == Entry.NULL) {
+ // Empty slot -> use this index
+ index = i;
+ break;
+ } else if (entry.generation <= generation && key.equals(entry.key)) {
+ // Key exists and generation is greater or equal -> use this index and boost the cost
+ index = i;
+ initialCost = entry.cost;
+ if (initialCost < Byte.MAX_VALUE) {
+ initialCost++;
+ }
+ break;
+ } else if (entry.generation < generation) {
+ // Old generation -> use this index
+ index = i;
+ break;
+ } else if (entry.cost < cheapest) {
+ // Candidate slot, keep on searching for even cheaper slots
+ cheapest = entry.cost;
+ index = i;
+ eviction = true;
+ }
+ }
+
+ if (index >= 0) {
+ Entry<?, ?> old = entries[index];
+ entries[index] = new Entry<>(key, value, generation, initialCost);
+ loadCount++;
+ costs[initialCost - Byte.MIN_VALUE]++;
+ if (old != Entry.NULL) {
+ costs[old.cost - Byte.MIN_VALUE]--;
+ if (eviction) {
+ evictions[old.cost - Byte.MIN_VALUE]++;
+ evictionCount++;
+ }
+ } else {
+ size++;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Look up a mapping from this cache by its {@code key} and {@code generation}.
+ * @param key key of the mapping to look up
+ * @param generation generation of the mapping to look up
+ * @return the mapping for {@code key} and {@code generation} or {@code null} if this
+ * cache does not contain such a mapping.
+ */
+ @SuppressWarnings("unchecked")
+ @CheckForNull
+ public synchronized V get(@Nonnull K key, int generation) {
+ int hashCode = key.hashCode();
+ for (int k = 0; k <= rehash; k++) {
+ int i = project(hashCode, k);
+ Entry<?, ?> entry = entries[i];
+ if (generation == entry.generation && key.equals(entry.key)) {
+ if (entry.cost < Byte.MAX_VALUE) {
+ costs[entry.cost - Byte.MIN_VALUE]--;
+ entry.cost++;
+ costs[entry.cost - Byte.MIN_VALUE]++;
+ }
+ hitCount++;
+ return (V) entry.value;
+ }
+ }
+ missCount++;
+ return null;
+ }
+
+ @Override
+ public synchronized String toString() {
+ return "PriorityCache" +
+ "{ costs=" + toString(costs) +
+ ", evictions=" + toString(evictions) + " }";
+ }
+
+ private static String toString(int[] ints) {
+ StringBuilder b = new StringBuilder("[");
+ String sep = "";
+ for (int i = 0; i < ints.length; i++) {
+ if (ints[i] > 0) {
+ b.append(sep).append(i).append("->").append(ints[i]);
+ sep = ",";
+ }
+ }
+ return b.append(']').toString();
+ }
+
+ /**
+ * @return access statistics for this cache
+ */
+ @Nonnull
+ public CacheStats getStats() {
+ return new CacheStats(hitCount, missCount, loadCount, 0, 0, evictionCount);
+ }
+
+}
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/NodeRecordTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/NodeRecordTest.java?rev=1760372&r1=1760371&r2=1760372&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/NodeRecordTest.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/NodeRecordTest.java Mon Sep 12 14:06:43 2016
@@ -186,7 +186,6 @@ public class NodeRecordTest {
public NodeCache getNodeCache(int generation) {
return defaultCache.getNodeCache(generation);
}
-
};
}
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/WriteCacheManagerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/WriteCacheManagerTest.java?rev=1760372&r1=1760371&r2=1760372&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/WriteCacheManagerTest.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/WriteCacheManagerTest.java Mon Sep 12 14:06:43 2016
@@ -21,9 +21,7 @@ package org.apache.jackrabbit.oak.segmen
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-import com.google.common.base.Supplier;
import org.apache.jackrabbit.oak.segment.WriterCacheManager.Default;
import org.apache.jackrabbit.oak.segment.WriterCacheManager.Empty;
import org.junit.Test;
@@ -34,7 +32,6 @@ public class WriteCacheManagerTest {
public void emptyGenerations() {
WriterCacheManager cache = Empty.INSTANCE;
assertEquals(cache.getTemplateCache(0), cache.getTemplateCache(1));
- assertEquals(cache.getNodeCache(0), cache.getNodeCache(1));
assertEquals(cache.getStringCache(0), cache.getStringCache(1));
}
@@ -42,26 +39,7 @@ public class WriteCacheManagerTest {
public void nonEmptyGenerations() {
WriterCacheManager cache = new Default();
assertNotEquals(cache.getTemplateCache(0), cache.getTemplateCache(1));
- assertNotEquals(cache.getNodeCache(0), cache.getNodeCache(1));
assertNotEquals(cache.getStringCache(0), cache.getStringCache(1));
}
- @Test
- public void factory() {
- WriterCacheManager cache = new Default(new Supplier<RecordCache<String>>() {
- int accessCount = 2;
- @Override
- public RecordCache<String> get() {
- assertTrue("Factory should only be invoked once per generation", --accessCount >= 0);
- return RecordCache.<String>factory(42).get();
- }
- }, RecordCache.<Template>factory(42), NodeCache.factory(42, 2));
-
- cache.getStringCache(0);
- cache.getStringCache(0);
- cache.getStringCache(1);
- cache.getStringCache(1);
- cache.getStringCache(1);
- cache.getStringCache(0);
- }
}
Added: jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/PriorityCacheTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/PriorityCacheTest.java?rev=1760372&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/PriorityCacheTest.java (added)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/PriorityCacheTest.java Mon Sep 12 14:06:43 2016
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.oak.segment.file;
+
+import static java.lang.Integer.valueOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class PriorityCacheTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void illegalSize() {
+ new PriorityCache<String, String>(42, 0);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void zeroSize() {
+ new PriorityCache<String, String>(0, 0);
+ }
+
+ @Test
+ public void legalHash() {
+ for (int k = 0; k < 8; k++) {
+ new PriorityCache<String, String>(0x1000000, k);
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void illegalHash() {
+ new PriorityCache<String, String>(0x1000000, 9);
+ }
+
+ @Test
+ public void singletonCache() {
+ PriorityCache<String, Integer> cache = new PriorityCache<String, Integer>(1, 0);
+ assertTrue(cache.put("one", 1, 0, (byte) 0));
+
+ // Cache is full -> cannot put another key of the same cost
+ assertFalse(cache.put("two", 2, 0, (byte) 0));
+
+ // Retrieving "one" leads to a cache hit increasing this key's cost to 1
+ assertEquals(valueOf(1), cache.get("one", 0));
+ assertNull(cache.get("one", 1));
+ assertNull(cache.get("two", 0));
+
+ // Inserting "two" only succeeds for cost 2, which is bigger than "one"'s cost of 1
+ assertFalse(cache.put("two", 2, 0, (byte) 1));
+ assertTrue(cache.put("two", 2, 0, (byte) 2));
+ assertEquals(valueOf(2), cache.get("two", 0));
+ assertNull(cache.get("two", 1));
+ assertNull(cache.get("one", 0));
+ }
+
+ @Test
+ public void readWrite() {
+ PriorityCache<String, Integer> cache = new PriorityCache<String, Integer>(128, 0);
+ for (int k = 0; k < 128; k++) {
+ if (cache.put("key-" + k, k, 0, (byte) 0)) {
+ assertEquals(Integer.valueOf(k), cache.get("key-" + k, 0));
+ assertNull(cache.get("key-" + k, 1));
+ } else {
+ assertNull(cache.get("key-" + k, 0));
+ }
+ }
+
+ for (int k = 0; k < 128; k++) {
+ Integer value = cache.get("key-" + k, 0);
+ if (value != null) {
+ assertEquals(Integer.valueOf(k), value);
+ }
+ }
+ }
+
+ @Test
+ public void updateKey() {
+ PriorityCache<String, Integer> cache = new PriorityCache<String, Integer>(1, 0);
+
+ assertTrue(cache.put("one", 1, 0, (byte) 0));
+
+ // Cache is full -> cannot put another key of the same cost
+ assertFalse(cache.put("two", 2, 0, (byte) 0));
+
+ // But updating an existing key works and boosts its cost to 1
+ assertTrue(cache.put("one", 1, 0, (byte) 0));
+
+ // such that adding another key only works at cost 2 and greater
+ assertFalse(cache.put("two", 2, 0, (byte) 1));
+ assertTrue(cache.put("two", 2, 0, (byte) 2));
+ }
+
+ @Test
+ public void updateWithNewGeneration() {
+ PriorityCache<String, Integer> cache = new PriorityCache<String, Integer>(1, 0);
+ assertTrue(cache.put("one", 1, 0, (byte) 0));
+
+ // Cache is full but we can still put a key of a higher generation
+ assertTrue(cache.put("two", 2, 1, (byte) 0));
+ assertNull(cache.get("one", 0));
+
+ // Cannot put a key of a lower generation
+ assertFalse(cache.put("two", 2, 0, (byte) 0));
+
+ // But one of the same generation
+ assertTrue(cache.put("two", 2, 1, (byte) 0));
+ }
+
+}
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java?rev=1760372&r1=1760371&r2=1760372&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java Mon Sep 12 14:06:43 2016
@@ -75,7 +75,7 @@ public class DataStoreTestBase extends T
return FileStoreBuilder.fileStoreBuilder(d)
.withMaxFileSize(1)
.withMemoryMapping(false)
- .withNodeDeduplicationCacheSize(0)
+ .withNodeDeduplicationCacheSize(1)
.withSegmentCacheSize(0)
.withStringCacheSize(0)
.withTemplateCacheSize(0)
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java?rev=1760372&r1=1760371&r2=1760372&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java Mon Sep 12 14:06:43 2016
@@ -84,7 +84,7 @@ public class TestBase {
return fileStoreBuilder(directory)
.withMaxFileSize(1)
.withMemoryMapping(false)
- .withNodeDeduplicationCacheSize(0)
+ .withNodeDeduplicationCacheSize(1)
.withSegmentCacheSize(0)
.withStringCacheSize(0)
.withTemplateCacheSize(0)
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/benchmark/BenchmarkBase.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/benchmark/BenchmarkBase.java?rev=1760372&r1=1760371&r2=1760372&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/benchmark/BenchmarkBase.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/standby/benchmark/BenchmarkBase.java Mon Sep 12 14:06:43 2016
@@ -78,9 +78,15 @@ public class BenchmarkBase {
}
private static FileStore newFileStore(File directory, ScheduledExecutorService executor) throws Exception {
- return fileStoreBuilder(directory).withMaxFileSize(1).withMemoryMapping(false).withNodeDeduplicationCacheSize(0)
- .withSegmentCacheSize(0).withStringCacheSize(0).withTemplateCacheSize(0)
- .withStatisticsProvider(new DefaultStatisticsProvider(executor)).build();
+ return fileStoreBuilder(directory)
+ .withMaxFileSize(1)
+ .withMemoryMapping(false)
+ .withNodeDeduplicationCacheSize(1)
+ .withSegmentCacheSize(0)
+ .withStringCacheSize(0)
+ .withTemplateCacheSize(0)
+ .withStatisticsProvider(new DefaultStatisticsProvider(executor))
+ .build();
}
protected FileStore setupPrimary(File directory, ScheduledExecutorService executor) throws Exception {