Author: mduerig
Date: Wed Apr 20 10:04:31 2016
New Revision: 1740092

URL: http://svn.apache.org/viewvc?rev=1740092&view=rev
Log:
OAK-3348: Cross gc sessions might introduce references to pre-compacted segments
* Id based equality: node states now carry an id. Optimise equality for blobs 
by checking equality of its block ids.

Modified:
    
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapRecord.java
    
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Record.java
    
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentBlob.java
    
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java
    
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java
    
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentParser.java
    
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/Template.java

Modified: 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapRecord.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapRecord.java?rev=1740092&r1=1740091&r2=1740092&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapRecord.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapRecord.java
 Wed Apr 20 10:04:31 2016
@@ -374,7 +374,7 @@ class MapRecord extends Record {
     }
 
     boolean compare(MapRecord before, final NodeStateDiff diff) {
-        if (fastEquals(this, before)) {
+        if (Record.fastEquals(this, before)) {
             return true;
         }
 

Modified: 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Record.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Record.java?rev=1740092&r1=1740091&r2=1740092&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Record.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Record.java
 Wed Apr 20 10:04:31 2016
@@ -27,12 +27,12 @@ class Record {
         return a instanceof Record && fastEquals((Record) a, b);
     }
 
-    static boolean fastEquals(Record a, Object b) {
+    private static boolean fastEquals(@Nonnull Record a, Object b) {
         return b instanceof Record && fastEquals(a, (Record) b);
     }
 
-    static boolean fastEquals(Record a, Record b) {
-        return a.offset == b.offset && a.segmentId.equals(b.segmentId);
+    private static boolean fastEquals(@Nonnull Record a, @Nonnull Record b) {
+        return a == b || (a.offset == b.offset && 
a.segmentId.equals(b.segmentId));
     }
 
     /**

Modified: 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentBlob.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentBlob.java?rev=1740092&r1=1740091&r2=1740092&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentBlob.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentBlob.java
 Wed Apr 20 10:04:31 2016
@@ -26,6 +26,7 @@ import static org.apache.jackrabbit.oak.
 import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.List;
 import java.util.Set;
 
 import javax.annotation.CheckForNull;
@@ -206,13 +207,29 @@ public class SegmentBlob extends Record
 
     @Override
     public boolean equals(Object object) {
-        if (object == this || fastEquals(this, object)) {
+        if (Record.fastEquals(this, object)) {
             return true;
         }
+
+        if (object instanceof SegmentBlob) {
+            SegmentBlob that = (SegmentBlob) object;
+            if (this.length() != that.length()) {
+                return false;
+            }
+            List<RecordId> bulkIds = this.getBulkRecordIds();
+            if (bulkIds != null && bulkIds.equals(that.getBulkRecordIds())) {
+                return true;
+            }
+        }
+
         return object instanceof Blob
                 && AbstractBlob.equal(this, (Blob) object);
     }
 
+    private static boolean isLongBlob(byte head) {
+        return (head & 0xe0) == 0xc0;
+    }
+
     @Override
     public int hashCode() {
         return 0;
@@ -232,7 +249,7 @@ public class SegmentBlob extends Record
         return Segment.readString(blobIdRecordId);
     }
 
-    private Iterable<SegmentId> getBulkSegmentIds() {
+    private List<RecordId> getBulkRecordIds() {
         Segment segment = getSegment();
         int offset = getOffset();
         byte head = segment.readByte(offset);
@@ -241,14 +258,23 @@ public class SegmentBlob extends Record
             long length = (segment.readLong(offset) & 0x1fffffffffffffffL) + 
MEDIUM_LIMIT;
             int listSize = (int) ((length + BLOCK_SIZE - 1) / BLOCK_SIZE);
             ListRecord list = new ListRecord(
-                    segment.readRecordId(offset + 8), listSize);
+                segment.readRecordId(offset + 8), listSize);
+            return list.getEntries();
+        } else {
+            return null;
+        }
+    }
+
+    private Iterable<SegmentId> getBulkSegmentIds() {
+        List<RecordId> recordIds = getBulkRecordIds();
+        if (recordIds == null) {
+            return emptySet();
+        } else {
             Set<SegmentId> ids = newHashSet();
-            for (RecordId id : list.getEntries()) {
+            for (RecordId id : recordIds) {
                 ids.add(id.getSegmentId());
             }
             return ids;
-        } else {
-            return emptySet();
         }
     }
 

Modified: 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java?rev=1740092&r1=1740091&r2=1740092&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java
 Wed Apr 20 10:04:31 2016
@@ -67,7 +67,7 @@ public class SegmentNodeState extends Re
         if (templateId == null) {
             // no problem if updated concurrently,
             // as each concurrent thread will just get the same value
-            templateId = getSegment().readRecordId(getOffset(0));
+            templateId = getSegment().readRecordId(getOffset(0, 1));
         }
         return templateId;
     }
@@ -83,7 +83,12 @@ public class SegmentNodeState extends Re
 
     MapRecord getChildNodeMap() {
         Segment segment = getSegment();
-        return segment.readMap(segment.readRecordId(getOffset(0, 1)));
+        return segment.readMap(segment.readRecordId(getOffset(0, 2)));
+    }
+
+    String getId() {
+        RecordId id = getSegment().readRecordId(getOffset());
+        return Segment.readString(id);
     }
 
     @Override
@@ -149,7 +154,7 @@ public class SegmentNodeState extends Re
 
     private RecordId getRecordIdV10(Segment segment, Template template,
             PropertyTemplate propertyTemplate) {
-        int ids = 1 + propertyTemplate.getIndex();
+        int ids = 2 + propertyTemplate.getIndex();
         if (template.getChildName() != Template.ZERO_CHILD_NODES) {
             ids++;
         }
@@ -158,7 +163,7 @@ public class SegmentNodeState extends Re
 
     private RecordId getRecordIdV11(Segment segment, Template template,
             PropertyTemplate propertyTemplate) {
-        int ids = 1;
+        int ids = 2;
         if (template.getChildName() != Template.ZERO_CHILD_NODES) {
             ids++;
         }
@@ -186,7 +191,7 @@ public class SegmentNodeState extends Re
         }
 
         Segment segment = getSegment();
-        int ids = 1;
+        int ids = 2;
         if (template.getChildName() != Template.ZERO_CHILD_NODES) {
             ids++;
         }
@@ -285,7 +290,7 @@ public class SegmentNodeState extends Re
 
         Segment segment = getSegment();
         RecordId id;
-        if (getSegment().getSegmentVersion().onOrAfter(V_11)) {
+        if (segment.getSegmentVersion().onOrAfter(V_11)) {
             id = getRecordIdV11(segment, template, propertyTemplate);
         } else {
             id = getRecordIdV10(segment, template, propertyTemplate);
@@ -387,7 +392,7 @@ public class SegmentNodeState extends Re
         } else if (childName != Template.ZERO_CHILD_NODES
                 && childName.equals(name)) {
             Segment segment = getSegment();
-            RecordId childNodeId = segment.readRecordId(getOffset(0, 1));
+            RecordId childNodeId = segment.readRecordId(getOffset(0, 2));
             return new SegmentNodeState(childNodeId);
         }
         checkValidName(name);
@@ -415,7 +420,7 @@ public class SegmentNodeState extends Re
             return getChildNodeMap().getEntries();
         } else {
             Segment segment = getSegment();
-            RecordId childNodeId = segment.readRecordId(getOffset(0, 1));
+            RecordId childNodeId = segment.readRecordId(getOffset(0, 2));
             return Collections.singletonList(new MemoryChildNodeEntry(
                     childName, new SegmentNodeState(childNodeId)));
         }
@@ -597,15 +602,35 @@ public class SegmentNodeState extends Re
 
     //------------------------------------------------------------< Object >--
 
+    static boolean fastEquals(NodeState a, NodeState b) {
+        if (Record.fastEquals(a, b)) {
+            return true;
+        }
+
+        if (a instanceof SegmentNodeState && b instanceof SegmentNodeState
+            && ((SegmentNodeState) a).getId().equals(((SegmentNodeState) 
b).getId())) {
+                return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return getId().hashCode();
+    }
+
     @Override
     public boolean equals(Object object) {
-        if (this == object || fastEquals(this, object)) {
-            return true;
-        } else if (object instanceof SegmentNodeState) {
+        if (object instanceof SegmentNodeState) {
             SegmentNodeState that = (SegmentNodeState) object;
-            Template template = getTemplate();
-            return template.equals(that.getTemplate())
+            if (fastEquals(this, that)) {
+                return true;
+            } else {
+                Template template = getTemplate();
+                return template.equals(that.getTemplate())
                     && template.compare(getRecordId(), that.getRecordId());
+            }
         } else {
             return object instanceof NodeState
                     && AbstractNodeState.equals(this, (NodeState) object); // 
TODO

Modified: 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java?rev=1740092&r1=1740091&r2=1740092&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java
 Wed Apr 20 10:04:31 2016
@@ -27,7 +27,6 @@ import static java.util.concurrent.TimeU
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.apache.jackrabbit.oak.api.Type.LONG;
 import static org.apache.jackrabbit.oak.api.Type.STRING;
-import static org.apache.jackrabbit.oak.plugins.segment.Record.fastEquals;
 import static 
org.apache.jackrabbit.oak.plugins.segment.compaction.CompactionStrategy.NO_COMPACTION;
 
 import java.io.Closeable;
@@ -306,7 +305,7 @@ public class SegmentNodeStore implements
 
         NodeState root = getRoot();
         NodeState before = snb.getBaseState();
-        if (!fastEquals(before, root)) {
+        if (!SegmentNodeState.fastEquals(before, root)) {
             SegmentNodeState after = snb.getNodeState();
             snb.reset(root);
             after.compareAgainstBaseState(
@@ -530,7 +529,7 @@ public class SegmentNodeStore implements
 
         private SegmentNodeBuilder prepare(SegmentNodeState state) throws 
CommitFailedException {
             SegmentNodeBuilder builder = state.builder();
-            if (fastEquals(before, state.getChildNode(ROOT))) {
+            if (SegmentNodeState.fastEquals(before, state.getChildNode(ROOT))) 
{
                 // use a shortcut when there are no external changes
                 builder.setChildNode(
                         ROOT, hook.processCommit(before, after, info));
@@ -618,7 +617,7 @@ public class SegmentNodeStore implements
         NodeState execute()
                 throws CommitFailedException, InterruptedException {
             // only do the merge if there are some changes to commit
-            if (!fastEquals(before, after)) {
+            if (!SegmentNodeState.fastEquals(before, after)) {
                 long timeout = optimisticMerge();
                 if (timeout >= 0) {
                     pessimisticMerge(timeout);

Modified: 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentParser.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentParser.java?rev=1740092&r1=1740091&r2=1740092&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentParser.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentParser.java
 Wed Apr 20 10:04:31 2016
@@ -410,6 +410,8 @@ public class SegmentParser {
 
         Segment segment = nodeId.getSegment();
         int offset = nodeId.getOffset();
+        //segment.readRecordId(offset);
+        offset += RECORD_ID_BYTES;
         RecordId templateId = segment.readRecordId(offset);
         onTemplate(nodeId, templateId);
 

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=1740092&r1=1740091&r2=1740092&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:04:31 2016
@@ -30,6 +30,7 @@ import static com.google.common.collect.
 import static com.google.common.collect.Lists.partition;
 import static com.google.common.collect.Maps.newHashMap;
 import static com.google.common.io.ByteStreams.read;
+import static java.lang.String.valueOf;
 import static java.util.Arrays.asList;
 import static java.util.Collections.emptyMap;
 import static java.util.Collections.nCopies;
@@ -62,6 +63,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
 
 import javax.jcr.PropertyType;
 
@@ -767,17 +769,20 @@ public class SegmentWriter {
                 }
             }
 
+            List<RecordId> ids = newArrayList();
+            if (state instanceof SegmentNodeState) {
+                ids.add(writeString(((SegmentNodeState) state).getId()));
+            } else {
+                ids.add(writeString(createId()));
+            }
+
             Template template = new Template(state);
-            RecordId templateId;
             if (template.equals(beforeTemplate)) {
-                templateId = before.getTemplateId();
+                ids.add(before.getTemplateId());
             } else {
-                templateId = writeTemplate(template);
+                ids.add(writeTemplate(template));
             }
 
-            List<RecordId> ids = newArrayList();
-            ids.add(templateId);
-
             String childName = template.getChildName();
             if (childName == Template.MANY_CHILD_NODES) {
                 MapRecord base;
@@ -911,6 +916,11 @@ public class SegmentWriter {
         return store.getTracker();
     }
 
+    private static final AtomicLong NEXT_ID = new AtomicLong();
+    private static String createId() {
+        return valueOf(NEXT_ID.getAndIncrement());
+    }
+
     private static final class Key<T> {
         private final T t;
         private final int generation;

Modified: 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Template.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Template.java?rev=1740092&r1=1740091&r2=1740092&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Template.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-segment-next/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Template.java
 Wed Apr 20 10:04:31 2016
@@ -20,7 +20,6 @@ import static com.google.common.base.Pre
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static 
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
-import static org.apache.jackrabbit.oak.plugins.segment.Record.fastEquals;
 import static 
org.apache.jackrabbit.oak.plugins.segment.Segment.RECORD_ID_BYTES;
 import static org.apache.jackrabbit.oak.plugins.segment.SegmentVersion.V_11;
 
@@ -33,7 +32,6 @@ import javax.annotation.Nonnull;
 
 import com.google.common.base.Objects;
 import com.google.common.collect.Lists;
-
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
@@ -180,7 +178,7 @@ public class Template {
         checkElementIndex(index, properties.length);
         Segment segment = checkNotNull(recordId).getSegment();
 
-        int offset = recordId.getOffset() + RECORD_ID_BYTES;
+        int offset = recordId.getOffset() + 2 * RECORD_ID_BYTES;
         if (childName != ZERO_CHILD_NODES) {
             offset += RECORD_ID_BYTES;
         }
@@ -199,7 +197,7 @@ public class Template {
     MapRecord getChildNodeMap(RecordId recordId) {
         checkState(childName != ZERO_CHILD_NODES);
         Segment segment = recordId.getSegment();
-        int offset = recordId.getOffset() + RECORD_ID_BYTES;
+        int offset = recordId.getOffset() + 2 * RECORD_ID_BYTES;
         RecordId childNodesId = segment.readRecordId(offset);
         return segment.readMap(childNodesId);
     }
@@ -217,7 +215,7 @@ public class Template {
             }
         } else if (name.equals(childName)) {
             Segment segment = recordId.getSegment();
-            int offset = recordId.getOffset() + RECORD_ID_BYTES;
+            int offset = recordId.getOffset() + 2 * RECORD_ID_BYTES;
             RecordId childNodeId = segment.readRecordId(offset);
             return new SegmentNodeState(childNodeId);
         } else {
@@ -233,7 +231,7 @@ public class Template {
             return map.getEntries();
         } else {
             Segment segment = recordId.getSegment();
-            int offset = recordId.getOffset() + RECORD_ID_BYTES;
+            int offset = recordId.getOffset() + 2 * RECORD_ID_BYTES;
             RecordId childNodeId = segment.readRecordId(offset);
             return Collections.singletonList(new MemoryChildNodeEntry(
                     childName, new SegmentNodeState(childNodeId)));
@@ -264,7 +262,7 @@ public class Template {
             // TODO: Leverage the HAMT data structure for the comparison
             MapRecord thisMap = getChildNodeMap(thisId);
             MapRecord thatMap = getChildNodeMap(thatId);
-            if (fastEquals(thisMap, thatMap)) {
+            if (Record.fastEquals(thisMap, thatMap)) {
                 return true; // shortcut
             } else if (thisMap.size() != thatMap.size()) {
                 return false; // shortcut


Reply via email to