Author: jukka
Date: Mon Mar 17 18:50:39 2014
New Revision: 1578510
URL: http://svn.apache.org/r1578510
Log:
OAK-1423: SegmentMK: Add index(es) to tar file backend
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/MappedAccess.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/RandomAccess.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/TarEntry.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/TarFile.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/TarFileTest.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java?rev=1578510&r1=1578509&r2=1578510&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java
Mon Mar 17 18:50:39 2014
@@ -57,6 +57,9 @@ public class FileStore implements Segmen
private static final String JOURNAL_FILE_NAME = "journal.log";
+ private static final boolean MEMORY_MAPPING_DEFAULT =
+ "64".equals(System.getProperty("sun.arch.data.model", "32"));
+
private final SegmentTracker tracker;
private final File directory;
@@ -106,6 +109,11 @@ public class FileStore implements Segmen
this(null, directory, maxFileSizeMB, memoryMapping);
}
+ public FileStore(File directory, int maxFileSizeMB)
+ throws IOException {
+ this(null, directory, maxFileSizeMB, MEMORY_MAPPING_DEFAULT);
+ }
+
public FileStore(File directory, int maxFileSizeMB, int cacheSizeMB,
boolean memoryMapping) throws IOException {
this(null, directory, EMPTY_NODE, maxFileSizeMB, cacheSizeMB,
memoryMapping);
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/MappedAccess.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/MappedAccess.java?rev=1578510&r1=1578509&r2=1578510&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/MappedAccess.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/MappedAccess.java
Mon Mar 17 18:50:39 2014
@@ -18,7 +18,6 @@ package org.apache.jackrabbit.oak.plugin
import static java.nio.channels.FileChannel.MapMode.READ_WRITE;
-import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
@@ -30,17 +29,16 @@ class MappedAccess implements FileAccess
private boolean updated = false;
- MappedAccess(File file, int length) throws IOException {
- RandomAccessFile f = new RandomAccessFile(file, "rw");
+ MappedAccess(RandomAccessFile file, int length) throws IOException {
try {
- long l = f.length();
+ long l = file.length();
if (l == 0) { // it's a new file
l = length;
updated = true;
}
- buffer = f.getChannel().map(READ_WRITE, 0, l);
+ buffer = file.getChannel().map(READ_WRITE, 0, l);
} finally {
- f.close();
+ file.close();
}
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/RandomAccess.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/RandomAccess.java?rev=1578510&r1=1578509&r2=1578510&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/RandomAccess.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/RandomAccess.java
Mon Mar 17 18:50:39 2014
@@ -19,7 +19,6 @@ package org.apache.jackrabbit.oak.plugin
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
-import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
@@ -30,8 +29,8 @@ class RandomAccess implements FileAccess
private final RandomAccessFile file;
- RandomAccess(@Nonnull File file) throws IOException {
- this.file = new RandomAccessFile(checkNotNull(file), "rw");
+ RandomAccess(@Nonnull RandomAccessFile file) throws IOException {
+ this.file = checkNotNull(file);
}
@Override
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/TarEntry.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/TarEntry.java?rev=1578510&r1=1578509&r2=1578510&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/TarEntry.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/TarEntry.java
Mon Mar 17 18:50:39 2014
@@ -16,9 +16,6 @@
*/
package org.apache.jackrabbit.oak.plugins.segment.file;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
class TarEntry {
private final int offset;
@@ -30,8 +27,12 @@ class TarEntry {
this.size = size;
}
- ByteBuffer read(FileAccess access) throws IOException {
- return access.read(offset, size);
+ int offset() {
+ return offset;
+ }
+
+ int size() {
+ return size;
}
}
\ No newline at end of file
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/TarFile.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/TarFile.java?rev=1578510&r1=1578509&r2=1578510&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/TarFile.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/TarFile.java
Mon Mar 17 18:50:39 2014
@@ -19,12 +19,16 @@ package org.apache.jackrabbit.oak.plugin
import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Maps.newConcurrentMap;
+import static com.google.common.collect.Maps.newTreeMap;
import java.io.File;
import java.io.IOException;
+import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
import java.util.UUID;
class TarFile {
@@ -42,6 +46,8 @@ class TarFile {
private final int maxFileSize;
+ private final byte[] indexEntryName;
+
private final Map<UUID, TarEntry> entries = newConcurrentMap();
TarFile(File file, int maxFileSize, boolean memoryMapping)
@@ -49,46 +55,35 @@ class TarFile {
long len = file.length();
checkState(len <= Integer.MAX_VALUE);
this.maxFileSize = Math.max((int) len, maxFileSize);
+ checkState(maxFileSize % BLOCK_SIZE == 0);
+ checkState(maxFileSize > 5 * BLOCK_SIZE);
+ this.indexEntryName = (file.getName() + ".idx").getBytes(UTF_8);
this.file = file;
+ RandomAccessFile f = new RandomAccessFile(file, "rw");
if (memoryMapping) {
- this.access = new MappedAccess(file, this.maxFileSize);
+ this.access = new MappedAccess(f, this.maxFileSize);
} else {
- this.access = new RandomAccess(file);
+ this.access = new RandomAccess(f);
}
+
+ this.position = 0;
if (len == 0) {
- // allocate the full file by writing the last two blocks
+ // allocate the full file by writing one big index entry
+ writeEntryHeader(indexEntryName, maxFileSize - 3 * BLOCK_SIZE);
+ // zero-out the last four bytes to indicate an empty index
+ access.write(
+ maxFileSize - (ZERO_BYTES.length * 2 + 4),
+ ZERO_BYTES, 0, 4);
+ // tar format expects the last two blocks to be zero
access.write(
maxFileSize - ZERO_BYTES.length * 2,
ZERO_BYTES, 0, ZERO_BYTES.length);
access.write(
maxFileSize - ZERO_BYTES.length,
ZERO_BYTES, 0, ZERO_BYTES.length);
- }
-
- this.position = 0;
- while (position + BLOCK_SIZE <= len) {
- // read the tar header block
- ByteBuffer buffer = this.access.read(position, BLOCK_SIZE);
- String name = readString(buffer, 100);
- buffer.position(124);
- int size = readNumber(buffer, 12);
- // TODO: verify the checksum, magic, etc.?
-
- if (name.isEmpty() && size == 0) {
- break; // no more entries in this file
- } else if (position + BLOCK_SIZE + size > len) {
- break; // invalid entry, truncate the file at this point
- }
-
- try {
- UUID id = UUID.fromString(name);
- entries.put(id, new TarEntry(position + BLOCK_SIZE, size));
- } catch (IllegalArgumentException e) {
- throw new IOException("Unexpected tar entry: " + name);
- }
-
- position += (1 + (size + BLOCK_SIZE - 1) / BLOCK_SIZE) *
BLOCK_SIZE;
+ } else {
+ readIndex(len);
}
}
@@ -99,7 +94,7 @@ class TarFile {
ByteBuffer readEntry(UUID uuid) throws IOException {
TarEntry entry = entries.get(uuid);
if (entry != null) {
- return entry.read(access);
+ return access.read(entry.offset(), entry.size());
} else {
return null;
}
@@ -107,15 +102,46 @@ class TarFile {
synchronized boolean writeEntry(
UUID uuid, byte[] b, int offset, int size) throws IOException {
- if (position + BLOCK_SIZE + size > maxFileSize) {
+ if (position + BLOCK_SIZE + size + 4 * BLOCK_SIZE > maxFileSize) {
+ writeEntryHeader(
+ indexEntryName, maxFileSize - 3 * BLOCK_SIZE - position);
+ ByteBuffer index = ByteBuffer.allocate(entries.size() * 24 + 4);
+ SortedMap<UUID, TarEntry> sorted = newTreeMap();
+ sorted.putAll(entries);
+ for (Map.Entry<UUID, TarEntry> entry : sorted.entrySet()) {
+ index.putLong(entry.getKey().getMostSignificantBits());
+ index.putLong(entry.getKey().getLeastSignificantBits());
+ index.putInt(entry.getValue().offset());
+ index.putInt(entry.getValue().size());
+ }
+ index.putInt(sorted.size());
+ access.write(
+ maxFileSize - 2 * BLOCK_SIZE - index.capacity(),
+ index.array(), 0, index.capacity());
return false;
}
+ writeEntryHeader(uuid.toString().getBytes(UTF_8), size);
+ position += BLOCK_SIZE;
+
+ access.write(position, b, offset, size);
+ entries.put(uuid, new TarEntry(position, size));
+ position += size;
+
+ int padding = BLOCK_SIZE - position % BLOCK_SIZE;
+ if (padding < BLOCK_SIZE) {
+ access.write(position, ZERO_BYTES, 0, padding);
+ position += padding;
+ }
+
+ return true;
+ }
+
+ protected void writeEntryHeader(byte[] name, int size) throws IOException {
byte[] header = new byte[BLOCK_SIZE];
// File name
- byte[] n = uuid.toString().getBytes(UTF_8);
- System.arraycopy(n, 0, header, 0, n.length);
+ System.arraycopy(name, 0, header, 0, name.length);
// File mode
System.arraycopy(
@@ -161,20 +187,8 @@ class TarFile {
header, 148, 6);
header[154] = 0;
+ checkState(position % BLOCK_SIZE == 0);
access.write(position, header, 0, BLOCK_SIZE);
- position += BLOCK_SIZE;
-
- access.write(position, b, offset, size);
- entries.put(uuid, new TarEntry(position, size));
- position += size;
-
- int padding = BLOCK_SIZE - position % BLOCK_SIZE;
- if (padding < BLOCK_SIZE) {
- access.write(position, ZERO_BYTES, 0, padding);
- position += padding;
- }
-
- return true;
}
public void flush() throws IOException {
@@ -187,6 +201,34 @@ class TarFile {
access.close();
}
+ private void readIndex(long len) throws IOException {
+ while (position + BLOCK_SIZE <= len) {
+ // read the tar header block
+ ByteBuffer buffer = this.access.read(position, BLOCK_SIZE);
+ String name = readString(buffer, 100);
+ buffer.position(124);
+ int size = readNumber(buffer, 12);
+ // TODO: verify the checksum, magic, etc.?
+
+ if (name.isEmpty() && size == 0) {
+ break; // no more entries in this file
+ } else if (Arrays.equals(name.getBytes(UTF_8), indexEntryName)) {
+ break; // index entry encountered, so stop here
+ } else if (position + BLOCK_SIZE + size > len) {
+ break; // invalid entry, truncate the file at this point
+ }
+
+ try {
+ UUID id = UUID.fromString(name);
+ entries.put(id, new TarEntry(position + BLOCK_SIZE, size));
+ } catch (IllegalArgumentException e) {
+ throw new IOException("Unexpected tar entry: " + name);
+ }
+
+ position += (1 + (size + BLOCK_SIZE - 1) / BLOCK_SIZE) *
BLOCK_SIZE;
+ }
+ }
+
private static String readString(ByteBuffer buffer, int fieldSize) {
byte[] b = new byte[fieldSize];
buffer.get(b);
Modified:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/TarFileTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/TarFileTest.java?rev=1578510&r1=1578509&r2=1578510&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/TarFileTest.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/TarFileTest.java
Mon Mar 17 18:50:39 2014
@@ -44,8 +44,8 @@ public class TarFileTest {
@Test
public void testOpenClose() throws IOException {
- new TarFile(file, 1024, true).close();
- new TarFile(file, 1024, false).close();
+ new TarFile(file, 10240, true).close();
+ new TarFile(file, 10240, false).close();
}
@Test
@@ -53,7 +53,7 @@ public class TarFileTest {
UUID id = UUID.randomUUID();
byte[] data = "Hello, World!".getBytes(UTF_8);
- TarFile tar = new TarFile(file, 1024, false);
+ TarFile tar = new TarFile(file, 10240, false);
try {
tar.writeEntry(id, data, 0, data.length);
assertEquals(ByteBuffer.wrap(data), tar.readEntry(id));
@@ -61,9 +61,9 @@ public class TarFileTest {
tar.close();
}
- assertEquals(1024, file.length());
+ assertEquals(10240, file.length());
- tar = new TarFile(file, 1024, false);
+ tar = new TarFile(file, 10240, false);
try {
assertEquals(ByteBuffer.wrap(data), tar.readEntry(id));
} finally {