提交 115f51dc authored 作者: Thomas Mueller's avatar Thomas Mueller

Shrink headers

上级 4c8e3aa0
...@@ -15,14 +15,6 @@ import java.util.HashMap; ...@@ -15,14 +15,6 @@ import java.util.HashMap;
* Chunks are page aligned (each page is usually 4096 bytes). * Chunks are page aligned (each page is usually 4096 bytes).
* There are at most 67 million (2^26) chunks, * There are at most 67 million (2^26) chunks,
* each chunk is at most 2 GB large. * each chunk is at most 2 GB large.
* Chunk format:
* 1 byte: 'c'
* 4 bytes: length
* 4 bytes: chunk id (an incrementing number)
* 4 bytes: pageCount
* 8 bytes: metaRootPos
* 8 bytes: maxLengthLive
* [ Page ] *
*/ */
public class Chunk { public class Chunk {
...@@ -37,14 +29,14 @@ public class Chunk { ...@@ -37,14 +29,14 @@ public class Chunk {
public final int id; public final int id;
/** /**
* The start position within the file. * The start block number within the file.
*/ */
public long start; public long block;
/** /**
* The length in bytes. * The length in number of blocks.
*/ */
public int length; public int blocks;
/** /**
* The total number of pages in this chunk. * The total number of pages in this chunk.
...@@ -122,12 +114,20 @@ public class Chunk { ...@@ -122,12 +114,20 @@ public class Chunk {
* *
* @param buff the target buffer * @param buff the target buffer
*/ */
void writeHeader(WriteBuffer buff) { void writeHeader(WriteBuffer buff, int minLength) {
long pos = buff.position();
buff.put((byte) '{'); buff.put((byte) '{');
buff.put(asString().getBytes(DataUtils.UTF8)); buff.put(asString().getBytes(DataUtils.UTF8));
buff.put((byte) '}'); buff.put((byte) '}');
while (buff.position() - pos < minLength - 1) {
buff.put((byte) ' '); buff.put((byte) ' ');
} }
buff.put((byte) '\n');
}
static String getMetaKey(int chunkId) {
return "chunk." + Integer.toHexString(chunkId);
}
/** /**
* Build a block from the given string. * Build a block from the given string.
...@@ -137,17 +137,17 @@ public class Chunk { ...@@ -137,17 +137,17 @@ public class Chunk {
*/ */
public static Chunk fromString(String s) { public static Chunk fromString(String s) {
HashMap<String, String> map = DataUtils.parseMap(s); HashMap<String, String> map = DataUtils.parseMap(s);
int id = Integer.parseInt(map.get("chunk")); int id = Integer.parseInt(map.get("chunk"), 16);
Chunk c = new Chunk(id); Chunk c = new Chunk(id);
c.start = Long.parseLong(map.get("start")); c.block = Long.parseLong(map.get("block"), 16);
c.length = Integer.parseInt(map.get("length")); c.blocks = Integer.parseInt(map.get("blocks"), 16);
c.pageCount = Integer.parseInt(map.get("pageCount")); c.pageCount = Integer.parseInt(map.get("pages"), 16);
c.pageCountLive = Integer.parseInt(map.get("pageCountLive")); c.pageCountLive = DataUtils.parseHexInt(map.get("livePages"), c.pageCount);
c.maxLength = Long.parseLong(map.get("maxLength")); c.maxLength = Long.parseLong(map.get("max"), 16);
c.maxLengthLive = Long.parseLong(map.get("maxLengthLive")); c.maxLengthLive = DataUtils.parseHexLong(map.get("liveMax"), c.maxLength);
c.metaRootPos = Long.parseLong(map.get("metaRoot")); c.metaRootPos = Long.parseLong(map.get("root"), 16);
c.time = Long.parseLong(map.get("time")); c.time = Long.parseLong(map.get("time"), 16);
c.version = Long.parseLong(map.get("version")); c.version = Long.parseLong(map.get("version"), 16);
return c; return c;
} }
...@@ -171,17 +171,22 @@ public class Chunk { ...@@ -171,17 +171,22 @@ public class Chunk {
* @return the string * @return the string
*/ */
public String asString() { public String asString() {
return StringBuilder buff = new StringBuilder();
"chunk:" + id + "," + buff.append("chunk:").append(Integer.toHexString(id)).
"length:" + length + "," + append(",block:").append(Long.toHexString(block)).
"maxLength:" + maxLength + "," + append(",blocks:").append(Integer.toHexString(blocks));
"maxLengthLive:" + maxLengthLive + "," + if (maxLength != maxLengthLive) {
"metaRoot:" + metaRootPos + "," + buff.append(",liveMax:").append(Long.toHexString(maxLengthLive));
"pageCount:" + pageCount + "," + }
"pageCountLive:" + pageCountLive + "," + if (pageCount != pageCountLive) {
"start:" + start + "," + buff.append(",livePages:").append(Integer.toHexString(pageCountLive));
"time:" + time + "," + }
"version:" + version; buff.append(",max:").append(Long.toHexString(maxLength)).
append(",pages:").append(Integer.toHexString(pageCount)).
append(",root:").append(Long.toHexString(metaRootPos)).
append(",time:").append(Long.toHexString(time)).
append(",version:").append(Long.toHexString(version));
return buff.toString();
} }
@Override @Override
......
...@@ -828,41 +828,42 @@ public class DataUtils { ...@@ -828,41 +828,42 @@ public class DataUtils {
} }
/** /**
* Parse a string as a number. * Parse a string as a hexadecimal number.
* *
* @param x the number * @param x the number
* @param defaultValue if x is null * @param defaultValue if x is null
* @return the parsed value * @return the parsed value
* @throws IllegalStateException if parsing fails * @throws IllegalStateException if parsing fails
*/ */
public static long parseLong(String x, long defaultValue) { public static long parseHexLong(String x, long defaultValue) {
if (x == null) { if (x == null) {
return defaultValue; return defaultValue;
} }
try { try {
return Long.parseLong(x); return Long.parseLong(x, 16);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw newIllegalStateException(ERROR_FILE_CORRUPT, throw newIllegalStateException(ERROR_FILE_CORRUPT,
"Error parsing the value {0} as a long", x, e); "Error parsing the value {0}", x, e);
} }
} }
/** /**
* Try to parse a string as a number. * Parse a string as a hexadecimal number.
* *
* @param x the number * @param x the number
* @param defaultValue if x is null * @param defaultValue if x is null
* @param errorValue if parsing fails * @return the parsed value
* @return the parsed value if parsing is possible * @throws IllegalStateException if parsing fails
*/ */
public static long parseLong(String x, long defaultValue, long errorValue) { public static int parseHexInt(String x, int defaultValue) {
if (x == null) { if (x == null) {
return defaultValue; return defaultValue;
} }
try { try {
return Long.parseLong(x); return Integer.parseInt(x, 16);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
return errorValue; throw newIllegalStateException(ERROR_FILE_CORRUPT,
"Error parsing the value {0}", x, e);
} }
} }
......
...@@ -72,9 +72,8 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -72,9 +72,8 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*/ */
protected void init(MVStore store, HashMap<String, String> config) { protected void init(MVStore store, HashMap<String, String> config) {
this.store = store; this.store = store;
this.id = Integer.parseInt(config.get("id")); this.id = Integer.parseInt(config.get("id"), 16);
String x = config.get("createVersion"); this.createVersion = DataUtils.parseHexLong(config.get("createVersion"), 0);
this.createVersion = x == null ? 0 : Long.parseLong(x);
this.writeVersion = store.getCurrentVersion(); this.writeVersion = store.getCurrentVersion();
} }
...@@ -1038,8 +1037,8 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1038,8 +1037,8 @@ public class MVMap<K, V> extends AbstractMap<K, V>
MVMap<K, V> m = new MVMap<K, V>(keyType, valueType); MVMap<K, V> m = new MVMap<K, V>(keyType, valueType);
m.readOnly = true; m.readOnly = true;
HashMap<String, String> config = New.hashMap(); HashMap<String, String> config = New.hashMap();
config.put("id", String.valueOf(id)); config.put("id", Integer.toHexString(id));
config.put("createVersion", String.valueOf(createVersion)); config.put("createVersion", Long.toHexString(createVersion));
m.init(store, config); m.init(store, config);
m.root = root; m.root = root;
return m; return m;
...@@ -1098,7 +1097,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1098,7 +1097,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
DataUtils.appendMap(buff, "name", name); DataUtils.appendMap(buff, "name", name);
} }
if (createVersion != 0) { if (createVersion != 0) {
DataUtils.appendMap(buff, "createVersion", createVersion); DataUtils.appendMap(buff, "createVersion", Long.toHexString(createVersion));
} }
String type = getType(); String type = getType();
if (type != null) { if (type != null) {
......
...@@ -67,18 +67,21 @@ public class MVStoreTool { ...@@ -67,18 +67,21 @@ public class MVStoreTool {
file = FilePath.get(fileName).open("r"); file = FilePath.get(fileName).open("r");
long fileLength = file.size(); long fileLength = file.size();
pw.println("file " + fileName); pw.println("file " + fileName);
pw.println(" length " + fileLength); pw.println(" length " + Long.toHexString(fileLength));
ByteBuffer block = ByteBuffer.allocate(4096); ByteBuffer block = ByteBuffer.allocate(4096);
for (long pos = 0; pos < fileLength;) { for (long pos = 0; pos < fileLength;) {
block.rewind(); block.rewind();
DataUtils.readFully(file, pos, block); DataUtils.readFully(file, pos, block);
block.rewind(); block.rewind();
if (block.get() != '{') {
block.position(MVStore.BLOCK_SIZE - MVStore.STORE_HEADER_LENGTH);
if (block.get() != '{') { if (block.get() != '{') {
continue; continue;
} }
}
byte headerType = block.get(); byte headerType = block.get();
if (headerType == 'H') { if (headerType == 'H') {
pw.println(" store header at " + pos); pw.println(" store header at " + Long.toHexString(pos));
pw.println(" " + new String(block.array(), "UTF-8").trim()); pw.println(" " + new String(block.array(), "UTF-8").trim());
pos += blockSize; pos += blockSize;
continue; continue;
...@@ -89,29 +92,32 @@ public class MVStoreTool { ...@@ -89,29 +92,32 @@ public class MVStoreTool {
} }
block.position(0); block.position(0);
Chunk c = Chunk.fromHeader(block, pos); Chunk c = Chunk.fromHeader(block, pos);
int chunkLength = c.length; int chunkLength = c.blocks * MVStore.BLOCK_SIZE;
pw.println(" " + c.toString()); pw.println(" " + c.toString());
ByteBuffer chunk = ByteBuffer.allocate(chunkLength); ByteBuffer chunk = ByteBuffer.allocate(chunkLength);
DataUtils.readFully(file, pos, chunk); DataUtils.readFully(file, pos, chunk);
int p = block.position(); int p = block.position();
pos = (pos + chunkLength + blockSize) / blockSize * blockSize; pos += chunkLength;
chunkLength -= p; int remaining = c.pageCount;
while (chunkLength > 0) { while (remaining > 0) {
chunk.position(p); chunk.position(p);
int pageLength = chunk.getInt(); int pageLength = chunk.getInt();
// check value (ignored) // check value (ignored)
chunk.getShort(); chunk.getShort();
long mapId = DataUtils.readVarInt(chunk); int mapId = DataUtils.readVarInt(chunk);
int len = DataUtils.readVarInt(chunk); int len = DataUtils.readVarInt(chunk);
int type = chunk.get(); int type = chunk.get();
boolean compressed = (type & 2) != 0; boolean compressed = (type & 2) != 0;
boolean node = (type & 1) != 0; boolean node = (type & 1) != 0;
pw.println(" map " + mapId + " at " + p + " " + pw.println(
(node ? "node" : "leaf") + " " + " map " + Integer.toHexString(mapId) +
(compressed ? "compressed " : "") + " at " + Long.toHexString(p) + " " +
"len: " + pageLength + " entries: " + len); (node ? " node" : " leaf") +
(compressed ? " compressed" : "") +
" len: " + Integer.toHexString(pageLength) +
" entries: " + Integer.toHexString(len));
p += pageLength; p += pageLength;
chunkLength -= pageLength; remaining--;
if (mapId == 0 && !compressed) { if (mapId == 0 && !compressed) {
String[] keys = new String[len]; String[] keys = new String[len];
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
...@@ -147,6 +153,12 @@ public class MVStoreTool { ...@@ -147,6 +153,12 @@ public class MVStoreTool {
} }
} }
} }
chunk.position(chunk.limit() - MVStore.STORE_HEADER_LENGTH);
if (chunk.get() == '{' && chunk.get() == 'H') {
pw.println(" store header");
pw.println(" " + new String(chunk.array(), chunk.position() - 2,
MVStore.STORE_HEADER_LENGTH, "UTF-8").trim());
}
} }
} catch (IOException e) { } catch (IOException e) {
pw.println("ERROR: " + e); pw.println("ERROR: " + e);
...@@ -165,8 +177,9 @@ public class MVStoreTool { ...@@ -165,8 +177,9 @@ public class MVStoreTool {
} }
private static String getPosString(long pos) { private static String getPosString(long pos) {
return "pos " + pos + ", chunk " + DataUtils.getPageChunkId(pos) + return "pos " + Long.toHexString(pos) +
", offset " + DataUtils.getPageOffset(pos); ", chunk " + Integer.toHexString(DataUtils.getPageChunkId(pos)) +
", offset " + Integer.toHexString(DataUtils.getPageOffset(pos));
} }
......
...@@ -260,13 +260,13 @@ public class Page { ...@@ -260,13 +260,13 @@ public class Page {
public String toString() { public String toString() {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
buff.append("id: ").append(System.identityHashCode(this)).append('\n'); buff.append("id: ").append(System.identityHashCode(this)).append('\n');
buff.append("pos: ").append(pos).append("\n"); buff.append("pos: ").append(Long.toHexString(pos)).append("\n");
for (int i = 0; i <= keyCount; i++) { for (int i = 0; i <= keyCount; i++) {
if (i > 0) { if (i > 0) {
buff.append(" "); buff.append(" ");
} }
if (children != null) { if (children != null) {
buff.append("[" + children[i] + "] "); buff.append("[" + Long.toHexString(children[i]) + "] ");
} }
if (i < keyCount) { if (i < keyCount) {
buff.append(keys[i]); buff.append(keys[i]);
......
...@@ -121,7 +121,8 @@ public class TestConcurrent extends TestMVStore { ...@@ -121,7 +121,8 @@ public class TestConcurrent extends TestMVStore {
chunkCount++; chunkCount++;
} }
} }
assertEquals(1, chunkCount); // the chunk metadata is not yet written
assertEquals(0, chunkCount);
s.close(); s.close();
} }
FileUtils.deleteRecursive("memFS:", false); FileUtils.deleteRecursive("memFS:", false);
......
...@@ -292,12 +292,12 @@ public class TestMVStore extends TestBase { ...@@ -292,12 +292,12 @@ public class TestMVStore extends TestBase {
}). }).
open(); open();
s.setAutoCommitDelay(10); s.setAutoCommitDelay(50);
MVMap<Integer, String> m; MVMap<Integer, String> m;
m = s.openMap("data"); m = s.openMap("data");
s.getFileStore().getFile().close(); s.getFileStore().getFile().close();
m.put(1, "Hello"); m.put(1, "Hello");
for (int i = 0; i < 100; i++) { for (int i = 0; i < 200; i++) {
if (exRef.get() != null) { if (exRef.get() != null) {
break; break;
} }
...@@ -597,7 +597,7 @@ public class TestMVStore extends TestBase { ...@@ -597,7 +597,7 @@ public class TestMVStore extends TestBase {
} }
s.close(); s.close();
int[] expectedReadsForCacheSize = { int[] expectedReadsForCacheSize = {
3407, 2590, 1924, 1440, 1096, 956, 918 3407, 2590, 1924, 1440, 1111, 956, 918
}; };
for (int cacheSize = 0; cacheSize <= 6; cacheSize += 4) { for (int cacheSize = 0; cacheSize <= 6; cacheSize += 4) {
int cacheMB = 1 + 3 * cacheSize; int cacheMB = 1 + 3 * cacheSize;
...@@ -612,8 +612,10 @@ public class TestMVStore extends TestBase { ...@@ -612,8 +612,10 @@ public class TestMVStore extends TestBase {
assertEquals(10240, x.length()); assertEquals(10240, x.length());
} }
} }
assertEquals(expectedReadsForCacheSize[cacheSize], long readCount = s.getFileStore().getReadCount();
s.getFileStore().getReadCount()); int expected = expectedReadsForCacheSize[cacheSize];
assertTrue("reads: " + readCount + " expected: " + expected,
Math.abs(100 - (100 * expected / readCount)) < 5);
s.close(); s.close();
} }
...@@ -648,8 +650,8 @@ public class TestMVStore extends TestBase { ...@@ -648,8 +650,8 @@ public class TestMVStore extends TestBase {
MVStore s = openStore(fileName); MVStore s = openStore(fileName);
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
assertEquals("1", s.getStoreHeader().get("format")); assertEquals("1", s.getStoreHeader().get("format"));
long creationTime = Long.parseLong(s.getStoreHeader() long creationTime = Long.parseLong(
.get("creationTime")); s.getStoreHeader().get("created"), 16);
assertTrue(Math.abs(time - creationTime) < 100); assertTrue(Math.abs(time - creationTime) < 100);
s.getStoreHeader().put("test", "123"); s.getStoreHeader().put("test", "123");
MVMap<Integer, Integer> map = s.openMap("test"); MVMap<Integer, Integer> map = s.openMap("test");
...@@ -684,7 +686,6 @@ public class TestMVStore extends TestBase { ...@@ -684,7 +686,6 @@ public class TestMVStore extends TestBase {
} }
} }
s.close(); s.close();
FilePath f = FilePath.get(fileName); FilePath f = FilePath.get(fileName);
int blockSize = 4 * 1024; int blockSize = 4 * 1024;
// test corrupt file headers // test corrupt file headers
...@@ -692,7 +693,7 @@ public class TestMVStore extends TestBase { ...@@ -692,7 +693,7 @@ public class TestMVStore extends TestBase {
FileChannel fc = f.open("rw"); FileChannel fc = f.open("rw");
if (i == 0) { if (i == 0) {
// corrupt the last block (the end header) // corrupt the last block (the end header)
fc.truncate(fc.size() - 4096); fc.write(ByteBuffer.allocate(256), fc.size() - 256);
} }
ByteBuffer buff = ByteBuffer.allocate(4 * 1024); ByteBuffer buff = ByteBuffer.allocate(4 * 1024);
fc.read(buff, i); fc.read(buff, i);
...@@ -1137,7 +1138,7 @@ public class TestMVStore extends TestBase { ...@@ -1137,7 +1138,7 @@ public class TestMVStore extends TestBase {
assertEquals(0, m.size()); assertEquals(0, m.size());
s.commit(); s.commit();
// ensure only nodes are read, but not leaves // ensure only nodes are read, but not leaves
assertEquals(42, s.getFileStore().getReadCount()); assertEquals(40, s.getFileStore().getReadCount());
assertEquals(1, s.getFileStore().getWriteCount()); assertEquals(1, s.getFileStore().getWriteCount());
s.close(); s.close();
} }
...@@ -1295,7 +1296,6 @@ public class TestMVStore extends TestBase { ...@@ -1295,7 +1296,6 @@ public class TestMVStore extends TestBase {
data.put("2", "World"); data.put("2", "World");
s.commit(); s.commit();
assertEquals(1, s.getCurrentVersion()); assertEquals(1, s.getCurrentVersion());
assertTrue(m.containsKey("chunk.1"));
assertFalse(m.containsKey("chunk.2")); assertFalse(m.containsKey("chunk.2"));
assertEquals("[data]", s.getMapNames().toString()); assertEquals("[data]", s.getMapNames().toString());
...@@ -1305,20 +1305,18 @@ public class TestMVStore extends TestBase { ...@@ -1305,20 +1305,18 @@ public class TestMVStore extends TestBase {
String id = s.getMetaMap().get("name.data"); String id = s.getMetaMap().get("name.data");
assertEquals("name:data", m.get("map." + id)); assertEquals("name:data", m.get("map." + id));
assertTrue(m.containsKey("chunk.1"));
assertEquals("Hello", data.put("1", "Hallo")); assertEquals("Hello", data.put("1", "Hallo"));
s.commit(); s.commit();
assertEquals("name:data", m.get("map." + id)); assertEquals("name:data", m.get("map." + id));
assertTrue(m.get("root.1").length() > 0); assertTrue(m.get("root.1").length() > 0);
assertTrue(m.containsKey("chunk.1")); assertTrue(m.containsKey("chunk.1"));
assertTrue(m.containsKey("chunk.2"));
assertEquals(2, s.getCurrentVersion()); assertEquals(2, s.getCurrentVersion());
s.rollbackTo(1); s.rollbackTo(1);
assertEquals("Hello", data.get("1")); assertEquals("Hello", data.get("1"));
assertEquals("World", data.get("2")); assertEquals("World", data.get("2"));
assertTrue(m.containsKey("chunk.1")); assertFalse(m.containsKey("chunk.1"));
assertFalse(m.containsKey("chunk.2")); assertFalse(m.containsKey("chunk.2"));
s.close(); s.close();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论