Index: src/main/java/org/apache/jackrabbit/oak/plugins/segment/RecordUsageAnalyser.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/plugins/segment/RecordUsageAnalyser.java (revision 1660633)
+++ src/main/java/org/apache/jackrabbit/oak/plugins/segment/RecordUsageAnalyser.java (working copy)
@@ -32,6 +32,7 @@
import static org.apache.jackrabbit.oak.plugins.segment.SegmentWriter.BLOCK_SIZE;
import static org.apache.jackrabbit.oak.plugins.segment.Template.MANY_CHILD_NODES;
import static org.apache.jackrabbit.oak.plugins.segment.Template.ZERO_CHILD_NODES;
+import static org.apache.jackrabbit.oak.plugins.segment.SegmentVersion.V_11;
import java.util.Formatter;
import java.util.Map;
@@ -227,10 +228,25 @@
int ids = template.getChildName() == ZERO_CHILD_NODES ? 1 : 2;
nodeSize += ids * RECORD_ID_BYTES;
PropertyTemplate[] propertyTemplates = template.getPropertyTemplates();
- for (PropertyTemplate propertyTemplate : propertyTemplates) {
- nodeSize += RECORD_ID_BYTES;
- RecordId propertyId = segment.readRecordId(offset + ids++ * RECORD_ID_BYTES);
- analyseProperty(propertyId, propertyTemplate);
+ if (segment.getStorageFormatVersion().onOrAfter(V_11)) {
+ if (propertyTemplates.length > 0) {
+ nodeSize += RECORD_ID_BYTES;
+ RecordId id = segment.readRecordId(offset + ids * RECORD_ID_BYTES);
+ ListRecord pIds = new ListRecord(id,
+ propertyTemplates.length);
+ for (int i = 0; i < propertyTemplates.length; i++) {
+ RecordId propertyId = pIds.getEntry(i);
+ analyseProperty(propertyId, propertyTemplates[i]);
+ }
+ analyseList(id, propertyTemplates.length);
+ }
+ } else {
+ for (PropertyTemplate propertyTemplate : propertyTemplates) {
+ nodeSize += RECORD_ID_BYTES;
+ RecordId propertyId = segment.readRecordId(offset + ids++
+ * RECORD_ID_BYTES);
+ analyseProperty(propertyId, propertyTemplate);
+ }
}
}
}
@@ -238,6 +254,7 @@
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
+ @SuppressWarnings("resource")
Formatter formatter = new Formatter(sb);
formatter.format(
"%s in maps (%s leaf and branch records)%n",
@@ -295,11 +312,26 @@
size += Segment.RECORD_ID_BYTES;
}
- for (int i = 0; i < propertyCount; i++) {
- RecordId propertyNameId = segment.readRecordId(offset + size);
- size += Segment.RECORD_ID_BYTES;
- size++; // type
- analyseString(propertyNameId);
+ if (segment.getStorageFormatVersion().onOrAfter(V_11)) {
+ if (propertyCount > 0) {
+ RecordId listId = segment.readRecordId(offset + size);
+ ListRecord propertyNames = new ListRecord(listId,
+ propertyCount);
+ for (int i = 0; i < propertyCount; i++) {
+ RecordId propertyNameId = propertyNames.getEntry(i);
+ size += Segment.RECORD_ID_BYTES;
+ size++; // type
+ analyseString(propertyNameId);
+ }
+ analyseList(listId, propertyCount);
+ }
+ } else {
+ for (int i = 0; i < propertyCount; i++) {
+ RecordId propertyNameId = segment.readRecordId(offset + size);
+ size += Segment.RECORD_ID_BYTES;
+ size++; // type
+ analyseString(propertyNameId);
+ }
}
templateSize += size;
}
Index: src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java (revision 1660633)
+++ src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java (working copy)
@@ -22,6 +22,7 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Lists.newArrayListWithCapacity;
import static com.google.common.collect.Maps.newConcurrentMap;
+import static org.apache.jackrabbit.oak.plugins.segment.SegmentVersion.V_11;
import static org.apache.jackrabbit.oak.plugins.segment.SegmentWriter.BLOCK_SIZE;
import java.io.IOException;
@@ -52,14 +53,6 @@
public class Segment {
/**
- * Version of the segment storage format.
- *
- * - 10 = all Oak versions released so far
- *
- */
- public static final byte STORAGE_FORMAT_VERSION = 10;
-
- /**
* Number of bytes used for storing a record identifier. One byte
* is used for identifying the segment and two for the record offset
* within that segment.
@@ -117,6 +110,11 @@
private final ByteBuffer data;
/**
+ * Version of the segment storage format.
+ */
+ private final SegmentVersion version;
+
+ /**
* Referenced segment identifiers. Entries are initialized lazily in
* {@link #getRefId(int)}. Set to {@code null} for bulk segments.
*/
@@ -137,19 +135,25 @@
private volatile long accessed = 0;
public Segment(SegmentTracker tracker, SegmentId id, ByteBuffer data) {
+ this(tracker, id, data, V_11);
+ }
+
+ public Segment(SegmentTracker tracker, SegmentId id, ByteBuffer data, SegmentVersion version) {
this.tracker = checkNotNull(tracker);
this.id = checkNotNull(id);
this.data = checkNotNull(data);
-
if (id.isDataSegmentId()) {
+ byte segmentFormat = data.get(3);
checkState(data.get(0) == '0'
&& data.get(1) == 'a'
&& data.get(2) == 'K'
- && data.get(3) == STORAGE_FORMAT_VERSION);
+ && SegmentVersion.isValid(segmentFormat));
this.refids = new SegmentId[getRefCount()];
- refids[0] = id;
+ this.refids[0] = id;
+ this.version = SegmentVersion.fromByte(segmentFormat);
} else {
this.refids = null;
+ this.version = version;
}
}
@@ -157,9 +161,9 @@
this.tracker = checkNotNull(tracker);
this.id = tracker.newDataSegmentId();
this.data = ByteBuffer.wrap(checkNotNull(buffer));
-
this.refids = new SegmentId[SEGMENT_REFERENCE_LIMIT + 1];
- refids[0] = id;
+ this.refids[0] = id;
+ this.version = SegmentVersion.fromByte(buffer[3]);
}
void access() {
@@ -171,6 +175,10 @@
return accessed != 0;
}
+ SegmentVersion getStorageFormatVersion() {
+ return version;
+ }
+
/**
* Maps the given record offset to the respective position within the
* internal {@link #data} array. The validity of a record with the given
@@ -436,19 +444,41 @@
offset += Segment.RECORD_ID_BYTES;
}
- PropertyTemplate[] properties =
- new PropertyTemplate[propertyCount];
- for (int i = 0; i < properties.length; i++) {
+ PropertyTemplate[] properties;
+ if (version.onOrAfter(V_11)) {
+ properties = readPropsV11(propertyCount, offset);
+ } else {
+ properties = readPropsV10(propertyCount, offset);
+ }
+ return new Template(primaryType, mixinTypes, properties, childName);
+ }
+
+ private PropertyTemplate[] readPropsV10(int propertyCount, int offset) {
+ PropertyTemplate[] properties = new PropertyTemplate[propertyCount];
+ for (int i = 0; i < propertyCount; i++) {
RecordId propertyNameId = readRecordId(offset);
offset += Segment.RECORD_ID_BYTES;
byte type = readByte(offset++);
- properties[i] = new PropertyTemplate(
- i, readString(propertyNameId),
+ properties[i] = new PropertyTemplate(i, readString(propertyNameId),
Type.fromTag(Math.abs(type), type < 0));
}
+ return properties;
+ }
- return new Template(
- primaryType, mixinTypes, properties, childName);
+ private PropertyTemplate[] readPropsV11(int propertyCount, int offset) {
+ PropertyTemplate[] properties = new PropertyTemplate[propertyCount];
+ if (propertyCount > 0) {
+ RecordId id = readRecordId(offset);
+ ListRecord propertyNames = new ListRecord(id, properties.length);
+ offset += Segment.RECORD_ID_BYTES;
+ for (int i = 0; i < propertyCount; i++) {
+ byte type = readByte(offset++);
+ properties[i] = new PropertyTemplate(i,
+ readString(propertyNames.getEntry(i)), Type.fromTag(
+ Math.abs(type), type < 0));
+ }
+ }
+ return properties;
}
long readLength(RecordId id) {
Index: src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java (revision 1660633)
+++ src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java (working copy)
@@ -46,6 +46,7 @@
import static org.apache.jackrabbit.oak.api.Type.STRINGS;
import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
+import static org.apache.jackrabbit.oak.plugins.segment.SegmentVersion.V_11;
import static org.apache.jackrabbit.oak.spi.state.AbstractNodeState.checkValidName;
/**
@@ -134,17 +135,39 @@
template.getPropertyTemplate(name);
if (propertyTemplate != null) {
Segment segment = getSegment();
- int ids = 1 + propertyTemplate.getIndex();
- if (template.getChildName() != Template.ZERO_CHILD_NODES) {
- ids++;
+ RecordId id;
+ if (getSegment().getStorageFormatVersion().onOrAfter(V_11)) {
+ id = getRecordIdV11(segment, template, propertyTemplate);
+ } else {
+ id = getRecordIdV10(segment, template, propertyTemplate);
}
- return new SegmentPropertyState(
- segment.readRecordId(getOffset(0, ids)), propertyTemplate);
+ return new SegmentPropertyState(id, propertyTemplate);
} else {
return null;
}
}
+ private RecordId getRecordIdV10(Segment segment, Template template,
+ PropertyTemplate propertyTemplate) {
+ int ids = 1 + propertyTemplate.getIndex();
+ if (template.getChildName() != Template.ZERO_CHILD_NODES) {
+ ids++;
+ }
+ return segment.readRecordId(getOffset(0, ids));
+ }
+
+ private RecordId getRecordIdV11(Segment segment, Template template,
+ PropertyTemplate propertyTemplate) {
+ int ids = 1;
+ if (template.getChildName() != Template.ZERO_CHILD_NODES) {
+ ids++;
+ }
+ RecordId rid = segment.readRecordId(getOffset(0, ids));
+ ListRecord pIds = new ListRecord(rid,
+ template.getPropertyTemplates().length);
+ return pIds.getEntry(propertyTemplate.getIndex());
+ }
+
@Override @Nonnull
public Iterable getProperties() {
Template template = getTemplate();
@@ -167,10 +190,24 @@
if (template.getChildName() != Template.ZERO_CHILD_NODES) {
ids++;
}
- for (int i = 0; i < propertyTemplates.length; i++) {
- RecordId propertyId = segment.readRecordId(getOffset(0, ids++));
- list.add(new SegmentPropertyState(
- propertyId, propertyTemplates[i]));
+
+ if (segment.getStorageFormatVersion().onOrAfter(V_11)) {
+ if (propertyTemplates.length > 0) {
+ ListRecord pIds = new ListRecord(
+ segment.readRecordId(getOffset(0, ids)),
+ propertyTemplates.length);
+ for (int i = 0; i < propertyTemplates.length; i++) {
+ RecordId propertyId = pIds.getEntry(i);
+ list.add(new SegmentPropertyState(propertyId,
+ propertyTemplates[i]));
+ }
+ }
+ } else {
+ for (int i = 0; i < propertyTemplates.length; i++) {
+ RecordId propertyId = segment.readRecordId(getOffset(0, ids++));
+ list.add(new SegmentPropertyState(propertyId,
+ propertyTemplates[i]));
+ }
}
return list;
@@ -247,11 +284,13 @@
}
Segment segment = getSegment();
- int ids = 1 + propertyTemplate.getIndex();
- if (template.getChildName() != Template.ZERO_CHILD_NODES) {
- ids++;
+ RecordId id;
+ if (getSegment().getStorageFormatVersion().onOrAfter(V_11)) {
+ id = getRecordIdV11(segment, template, propertyTemplate);
+ } else {
+ id = getRecordIdV10(segment, template, propertyTemplate);
}
- return segment.readString(segment.readRecordId(getOffset(0, ids)));
+ return segment.readString(id);
}
/**
@@ -288,12 +327,12 @@
}
Segment segment = getSegment();
- int ids = 1 + propertyTemplate.getIndex();
- if (template.getChildName() != Template.ZERO_CHILD_NODES) {
- ids++;
+ RecordId id;
+ if (getSegment().getStorageFormatVersion().onOrAfter(V_11)) {
+ id = getRecordIdV11(segment, template, propertyTemplate);
+ } else {
+ id = getRecordIdV10(segment, template, propertyTemplate);
}
-
- RecordId id = segment.readRecordId(getOffset(0, ids));
segment = id.getSegment();
int size = segment.readInt(id.getOffset());
if (size == 0) {
Index: src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentTracker.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentTracker.java (revision 1660633)
+++ src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentTracker.java (working copy)
@@ -93,20 +93,25 @@
private long currentSize = 0;
- public SegmentTracker(SegmentStore store, int cacheSizeMB) {
+ public SegmentTracker(SegmentStore store, int cacheSizeMB,
+ SegmentVersion version) {
for (int i = 0; i < tables.length; i++) {
tables[i] = new SegmentIdTable(this);
}
this.store = store;
- this.writer = new SegmentWriter(store, this);
+ this.writer = new SegmentWriter(store, this, version);
this.cacheSize = cacheSizeMB * MB;
this.compactionMap = new AtomicReference(
new CompactionMap(1, this));
}
+ public SegmentTracker(SegmentStore store, SegmentVersion version) {
+ this(store, DEFAULT_MEMORY_CACHE_SIZE, version);
+ }
+
public SegmentTracker(SegmentStore store) {
- this(store, DEFAULT_MEMORY_CACHE_SIZE);
+ this(store, DEFAULT_MEMORY_CACHE_SIZE, SegmentVersion.V_11);
}
public SegmentWriter getWriter() {
Index: src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentVersion.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentVersion.java (revision 0)
+++ src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentVersion.java (revision 0)
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+/**
+ * Version of the segment storage format.
+ *
+ * - 10 = all Oak versions previous to 11
+ * - 11 = all Oak versions starting from TODO
+ *
+ */
+public enum SegmentVersion {
+
+ /**
+ * @deprecated Use latest version V11
+ */
+ @Deprecated
+ V_10,
+
+ V_11;
+
+ public boolean onOrAfter(SegmentVersion other) {
+ return compareTo(other) >= 0;
+ }
+
+ public static byte asByte(SegmentVersion v) {
+ switch (v) {
+ case V_11:
+ return 11;
+ case V_10:
+ return 10;
+ }
+ throw new IllegalArgumentException("Unwnown version " + v);
+ }
+
+ public static SegmentVersion fromByte(byte v) {
+ switch (v) {
+ case 11:
+ return V_11;
+ case 10:
+ return V_10;
+ }
+ throw new IllegalArgumentException("Unwnown version " + v);
+ }
+
+ public static boolean isValid(byte v) {
+ return v == 10 || v == 11;
+ }
+
+}
Index: src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
===================================================================
--- src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java (revision 1660633)
+++ src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java (working copy)
@@ -38,7 +38,7 @@
import static org.apache.jackrabbit.oak.plugins.segment.Segment.MAX_SEGMENT_SIZE;
import static org.apache.jackrabbit.oak.plugins.segment.Segment.RECORD_ID_BYTES;
import static org.apache.jackrabbit.oak.plugins.segment.Segment.SEGMENT_REFERENCE_LIMIT;
-import static org.apache.jackrabbit.oak.plugins.segment.Segment.STORAGE_FORMAT_VERSION;
+import static org.apache.jackrabbit.oak.plugins.segment.SegmentVersion.V_11;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -90,12 +90,12 @@
static final int BLOCK_SIZE = 1 << 12; // 4kB
- static byte[] createNewBuffer() {
+ static byte[] createNewBuffer(SegmentVersion v) {
byte[] buffer = new byte[Segment.MAX_SEGMENT_SIZE];
buffer[0] = '0';
buffer[1] = 'a';
buffer[2] = 'K';
- buffer[3] = STORAGE_FORMAT_VERSION;
+ buffer[3] = SegmentVersion.asByte(v);
buffer[4] = 0; // reserved
buffer[5] = 0; // refcount
return buffer;
@@ -119,6 +119,7 @@
* avoid storing duplicates of frequently occurring data.
* Should only be accessed from synchronized blocks to prevent corruption.
*/
+ @SuppressWarnings("serial")
private final Map