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