Author: mduerig
Date: Wed Apr 20 10:03:45 2016
New Revision: 1740087
URL: http://svn.apache.org/viewvc?rev=1740087&view=rev
Log:
OAK-3348: Cross gc sessions might introduce references to pre-compacted segments
* Move record caches to top level
* Re-enable template and string caches and including gc generation in the cache
key, thus there is no need anymore to flush the caches on compaction.
Added:
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/RecordCache.java
Modified:
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
Added:
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/RecordCache.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/RecordCache.java?rev=1740087&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/RecordCache.java
(added)
+++
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/RecordCache.java
Wed Apr 20 10:03:45 2016
@@ -0,0 +1,85 @@
+/*
+ * 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.plugins.segment;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * FIXME OAK-3348 XXX document
+ */
+abstract class RecordCache<T> {
+ // FIXME OAK-3348 XXX this caches retain in mem refs to old gens. assess
impact and mitigate/fix
+ private static final RecordCache<?> DISABLED_CACHE = new
RecordCache<Object>() {
+ @Override
+ RecordId get(Object key) {
+ return null;
+ }
+
+ @Override
+ RecordId put(Object key, RecordId value) {
+ return null;
+ }
+
+ @Override
+ void clear() { }
+ };
+
+ @SuppressWarnings("unchecked")
+ static <T> RecordCache<T> newRecordCache(final int initialSize, final int
size, boolean disabled) {
+ if (disabled) {
+ return (RecordCache<T>) DISABLED_CACHE;
+ } else {
+ return new LRURecordCache<T>(initialSize, size);
+ }
+ }
+
+ abstract RecordId get(Object key);
+ abstract RecordId put(T key, RecordId value);
+ abstract void clear();
+
+ private static final class LRURecordCache<T> extends RecordCache<T> {
+ private final Map<T, RecordId> cache;
+
+ private LRURecordCache(int initialSize, final int size) {
+ cache = new LinkedHashMap<T, RecordId>(initialSize, 0.9f, true) {
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<T, RecordId>
eldest) {
+ return size() > size;
+ }
+ };
+ }
+
+ @Override
+ synchronized RecordId get(Object key) {
+ return cache.get(key);
+ }
+
+ @Override
+ synchronized RecordId put(T key, RecordId value) {
+ return cache.put(key, value);
+ }
+
+ @Override
+ public synchronized void clear() {
+ cache.clear();
+ }
+ }
+}
Modified:
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java?rev=1740087&r1=1740086&r2=1740087&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
(original)
+++
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
Wed Apr 20 10:03:45 2016
@@ -40,6 +40,7 @@ import static org.apache.jackrabbit.oak.
import static org.apache.jackrabbit.oak.api.Type.NAMES;
import static org.apache.jackrabbit.oak.api.Type.STRING;
import static
org.apache.jackrabbit.oak.plugins.segment.MapRecord.BUCKETS_PER_LEVEL;
+import static
org.apache.jackrabbit.oak.plugins.segment.RecordCache.newRecordCache;
import static
org.apache.jackrabbit.oak.plugins.segment.RecordWriters.newBlobIdWriter;
import static
org.apache.jackrabbit.oak.plugins.segment.RecordWriters.newBlockWriter;
import static
org.apache.jackrabbit.oak.plugins.segment.RecordWriters.newListBucketWriter;
@@ -60,12 +61,12 @@ import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Arrays;
import java.util.Collection;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.jcr.PropertyType;
+import com.google.common.base.Objects;
import com.google.common.io.Closeables;
import org.apache.jackrabbit.oak.api.Blob;
import org.apache.jackrabbit.oak.api.PropertyState;
@@ -94,8 +95,9 @@ public class SegmentWriter {
* Cache of recently stored string records, used to avoid storing
duplicates
* of frequently occurring data.
*/
- private final Map<String, RecordId> stringCache = newItemsCache(
- STRING_RECORDS_CACHE_SIZE);
+ private final RecordCache<Object> stringCache = newRecordCache(
+ STRING_RECORDS_CACHE_SIZE <= 0 ? 0 : (int) (STRING_RECORDS_CACHE_SIZE
* 1.2),
+ STRING_RECORDS_CACHE_SIZE, STRING_RECORDS_CACHE_SIZE <= 0);
private static final int TPL_RECORDS_CACHE_SIZE = Integer.getInteger(
"oak.segment.writer.templatesCacheSize", 3000);
@@ -104,7 +106,9 @@ public class SegmentWriter {
* Cache of recently stored template records, used to avoid storing
* duplicates of frequently occurring data.
*/
- private final Map<Template, RecordId> templateCache =
newItemsCache(TPL_RECORDS_CACHE_SIZE);
+ private final RecordCache<Object> templateCache = newRecordCache(
+ TPL_RECORDS_CACHE_SIZE <= 0 ? 0 : (int) (TPL_RECORDS_CACHE_SIZE * 1.2),
+ TPL_RECORDS_CACHE_SIZE, TPL_RECORDS_CACHE_SIZE <= 0);
private final SegmentStore store;
@@ -115,35 +119,6 @@ public class SegmentWriter {
private final SegmentBufferWriterPool segmentBufferWriterPool;
- private static final <T> Map<T, RecordId> newItemsCache(final int size) {
- final boolean disabled = true; // FIXME OAK-3348 re-enable caches but
make generation part of cache key to avoid backrefs
- final int safeSize = size <= 0 ? 0 : (int) (size * 1.2);
- return new LinkedHashMap<T, RecordId>(safeSize, 0.9f, true) {
- @Override
- protected boolean removeEldestEntry(Map.Entry<T, RecordId> e) {
- return size() > size;
- }
- @Override
- public synchronized RecordId get(Object key) {
- if (disabled) {
- return null;
- }
- return super.get(key);
- }
- @Override
- public synchronized RecordId put(T key, RecordId value) {
- if (disabled) {
- return null;
- }
- return super.put(key, value);
- }
- @Override
- public synchronized void clear() {
- super.clear();
- }
- };
- }
-
/**
* @param store store to write to
* @param version segment version to write
@@ -159,12 +134,6 @@ public class SegmentWriter {
segmentBufferWriterPool.flush();
}
- // FIXME OAK-3348 this is a hack and probably prone to races: replace with
making the tacker allocate a new writer
- public void dropCache() {
- stringCache.clear();
- templateCache.clear();
- }
-
MapRecord writeMap(MapRecord base, Map<String, RecordId> changes) throws
IOException {
Writer writer = new Writer();
try {
@@ -497,7 +466,7 @@ public class SegmentWriter {
* @return value record identifier
*/
private RecordId writeString(String string) throws IOException {
- RecordId id = stringCache.get(string);
+ RecordId id = stringCache.get(key(string));
if (id != null) {
return id; // shortcut if the same string was recently stored
}
@@ -507,7 +476,7 @@ public class SegmentWriter {
if (data.length < Segment.MEDIUM_LIMIT) {
// only cache short strings to avoid excessive memory use
id = writeValueRecord(data.length, data);
- stringCache.put(string, id);
+ stringCache.put(key(string), id);
return id;
}
@@ -691,7 +660,7 @@ public class SegmentWriter {
private RecordId writeTemplate(Template template) throws IOException {
checkNotNull(template);
- RecordId id = templateCache.get(template);
+ RecordId id = templateCache.get(key(template));
if (id != null) {
return id; // shortcut if the same template was recently stored
}
@@ -762,7 +731,7 @@ public class SegmentWriter {
RecordId tid = newTemplateWriter(ids, propertyNames,
propertyTypes, head, primaryId, mixinIds, childNameId,
propNamesId, version).write(writer);
- templateCache.put(template, tid);
+ templateCache.put(key(template), tid);
return tid;
}
@@ -904,6 +873,10 @@ public class SegmentWriter {
return thatGen < thisGen;
}
+ private <T> Key<T> key(T t) {
+ return new Key<T>(t, writer.getGeneration());
+ }
+
private class ChildNodeCollectorDiff extends DefaultNodeStateDiff {
private final Map<String, RecordId> childNodes = newHashMap();
private IOException exception;
@@ -952,4 +925,32 @@ public class SegmentWriter {
return store.getTracker();
}
+ private static final class Key<T> {
+ private final T t;
+ private final int generation;
+
+ private Key(T t, int generation) {
+ this.t = t;
+ this.generation = generation;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+
+ Key<?> that = (Key<?>) other;
+ return generation == that.generation && t.equals(that.t);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(t, generation);
+ }
+ }
+
}
Modified:
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java?rev=1740087&r1=1740086&r2=1740087&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
(original)
+++
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
Wed Apr 20 10:03:45 2016
@@ -1136,7 +1136,7 @@ public class FileStore implements Segmen
closeAndLogOnFail(diskSpaceThread);
try {
flush();
- tracker.getWriter().dropCache();
+ // FIXME OAK-3348 XXX replace with some sort of close call
tracker.getWriter().dropCache();
fileStoreLock.writeLock().lock();
try {
closeAndLogOnFail(writer);
@@ -1476,11 +1476,6 @@ public class FileStore implements Segmen
if (setHead(before, after)) {
tracker.setCompactionMap(compactor.getCompactionMap());
- // Drop the SegmentWriter caches and flush any existing state
- // in an attempt to prevent new references to old pre-compacted
- // content. TODO: There should be a cleaner way to do this.
(implement GCMonitor!?)
- tracker.getWriter().dropCache();
-
CompactionMap cm = tracker.getCompactionMap();
gcMonitor.compacted(cm.getSegmentCounts(),
cm.getRecordCounts(), cm.getEstimatedWeights());
tracker.clearSegmentIdTables(compactionStrategy);