提交 d056ce9c authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore bugfixes

上级 43e1956c
......@@ -108,6 +108,7 @@ MVStore:
(by truncating / deleting empty files)
- add new feature to file systems that avoid copying data
(reads should return a ByteBuffer, not write into one)
- do we need to store a dummy chunk entry in the chunk itself?
*/
......@@ -513,40 +514,50 @@ public class MVStore {
}
private void readMeta() {
chunks.clear();
Chunk header = readChunkHeader(rootChunkStart);
lastChunkId = header.id;
chunks.put(header.id, header);
meta.setRootPos(header.metaRootPos, -1);
// load chunks in reverse order, because data about previous chunks
// might only be available in later chunks
// if this is a performance problem when there are many
// chunks, the id of the previous / next chunk might need to
// be maintained
for (int id = lastChunkId; id >= 0; id--) {
String s = meta.get("chunk." + id);
if (s == null) {
continue;
String s = meta.get("chunk." + lastChunkId);
Chunk h2 = Chunk.fromString(s);
h2.start = header.start;
h2.length = header.length;
h2.metaRootPos = header.metaRootPos;
h2.pageCount = header.pageCount;
h2.pageCountLive = header.pageCountLive;
h2.maxLength = header.maxLength;
h2.maxLengthLive = header.maxLengthLive;
chunks.put(header.id, h2);
// we can load the chunk in any order,
// because loading chunk metadata
// might recursively load another chunk
for (Iterator<String> it = meta.keyIterator("chunk."); it.hasNext();) {
s = it.next();
if (!s.startsWith("chunk.")) {
break;
}
s = meta.get(s);
Chunk c = Chunk.fromString(s);
if (c.id == header.id) {
c.start = header.start;
c.length = header.length;
c.metaRootPos = header.metaRootPos;
c.pageCount = header.pageCount;
c.pageCountLive = header.pageCountLive;
c.maxLength = header.maxLength;
c.maxLengthLive = header.maxLengthLive;
if (!chunks.containsKey(c.id)) {
chunks.put(c.id, c);
}
lastChunkId = Math.max(c.id, lastChunkId);
chunks.put(c.id, c);
}
// build the free space list
for (Chunk c : chunks.values()) {
if (c.pageCountLive == 0) {
// remove this chunk in the next save operation
registerFreePage(currentVersion, c.id, 0, 0);
}
}
// build the free space list
for (Chunk c : chunks.values()) {
if (c.id > lastChunkId) {
System.out.println("strange!");
}
lastChunkId = Math.max(c.id, lastChunkId);
if (c.start == Long.MAX_VALUE) {
;;
System.out.println("??");
continue;
}
int len = MathUtils.roundUpInt(c.length, BLOCK_SIZE) + BLOCK_SIZE;
......@@ -711,7 +722,20 @@ public class MVStore {
* @return the chunk
*/
Chunk getChunk(long pos) {
return chunks.get(DataUtils.getPageChunkId(pos));
int chunkId = DataUtils.getPageChunkId(pos);
Chunk c = chunks.get(chunkId);
if (c == null) {
String s = meta.get("chunk." + chunkId);
if (s == null) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_FILE_CORRUPT,
"Chunk {0} not found",
DataUtils.getPageChunkId(pos));
}
c = Chunk.fromString(s);
chunks.put(c.id, c);
}
return c;
}
/**
......@@ -1358,7 +1382,7 @@ public class MVStore {
if (p == null) {
// was removed later - ignore
// or the chunk no longer exists
} else if (p.getPos() < 0) {
} else if (p.getPos() == 0) {
// temporarily changed - ok
// TODO move old data if there is an uncommitted change?
} else {
......@@ -1390,14 +1414,12 @@ public class MVStore {
Page p = cache.get(pos);
if (p == null) {
Chunk c = getChunk(pos);
if (c == null) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_FILE_CORRUPT,
"Chunk {0} not found",
DataUtils.getPageChunkId(pos));
}
long filePos = c.start;
filePos += DataUtils.getPageOffset(pos);
if (filePos < 0) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_FILE_CORRUPT, "Negative position {0}", filePos);
}
p = Page.read(fileStore, map, pos, filePos, fileStore.size());
cache.put(pos, p, p.getMemory());
}
......
......@@ -11,6 +11,8 @@ import java.io.PrintWriter;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import org.h2.mvstore.type.StringDataType;
import org.h2.store.fs.FilePath;
/**
......@@ -36,6 +38,15 @@ public class MVStoreTool {
}
}
}
/**
* Read the contents of the file and write them to system out.
*
* @param fileName the name of the file
*/
public static void dump(String fileName) {
dump(fileName, new PrintWriter(System.out));
}
/**
* Read the contents of the file and display them in a human-readable
......@@ -83,9 +94,7 @@ public class MVStoreTool {
" at " + pos +
" length " + chunkLength +
" pageCount " + pageCount +
" root " + metaRootPos +
" chunk " + DataUtils.getPageChunkId(metaRootPos) +
" offset " + DataUtils.getPageOffset(metaRootPos) +
" root " + getPosString(metaRootPos) +
" maxLength " + maxLength +
" maxLengthLive " + maxLengthLive);
ByteBuffer chunk = ByteBuffer.allocate(chunkLength);
......@@ -109,6 +118,40 @@ public class MVStoreTool {
"len: " + pageLength + " entries: " + len);
p += pageLength;
chunkLength -= pageLength;
if (mapId == 0 && !compressed) {
String[] keys = new String[len];
for (int i = 0; i < len; i++) {
String k = StringDataType.INSTANCE.read(chunk);
keys[i] = k;
}
if (node) {
long[] children = new long[len + 1];
for (int i = 0; i <= len; i++) {
children[i] = chunk.getLong();
}
long[] counts = new long[len + 1];
for (int i = 0; i <= len; i++) {
long s = DataUtils.readVarLong(chunk);
counts[i] = s;
}
for (int i = 0; i < len; i++) {
pw.println(" < " + keys[i] + ": " +
counts[i] + " -> " + getPosString(children[i]));
}
pw.println(" >= : " +
counts[len] + " -> " + getPosString(children[len]));
} else {
// meta map leaf
String[] values = new String[len];
for (int i = 0; i < len; i++) {
String v = StringDataType.INSTANCE.read(chunk);
values[i] = v;
}
for (int i = 0; i < len; i++) {
pw.println(" " + keys[i] + "=" + values[i]);
}
}
}
}
}
} catch (IOException e) {
......@@ -126,5 +169,11 @@ public class MVStoreTool {
pw.println();
pw.flush();
}
private static String getPosString(long pos) {
return "pos " + pos + ", chunk " + DataUtils.getPageChunkId(pos) +
", offset " + DataUtils.getPageOffset(pos);
}
}
......@@ -65,7 +65,10 @@ public class TestMVStore extends TestBase {
testCacheSize();
testConcurrentOpen();
testFileHeader();
int todoFixTestCase;
// testFileHeaderCorruption();
testIndexSkip();
testMinMaxNextKey();
testStoreVersion();
......
......@@ -66,6 +66,7 @@ public class TestRandomMapOps extends TestBase {
bestSeed = seed;
best = op;
failException = ex;
// System.out.println("seed:" + seed + " op:" + op);
}
}
if (failException != null) {
......@@ -77,15 +78,13 @@ public class TestRandomMapOps extends TestBase {
private void testCase() throws Exception {
FileUtils.delete(fileName);
MVStore s;
s = new MVStore.Builder().fileName(fileName).
pageSplitSize(50).writeDelay(0).open();
s = openStore(fileName);
MVMap<Integer, byte[]> m;
if (concurrent) {
m = s.openMap("data", new MVMapConcurrent.Builder<Integer, byte[]>());
} else {
m = s.openMap("data");
}
Random r = new Random(seed);
op = 0;
int size = getSize(100, 1000);
......@@ -99,48 +98,51 @@ public class TestRandomMapOps extends TestBase {
case 1:
case 2:
case 3:
log(op, k, v, "put");
log(op, k, v, "m.put({0}, {1})");
m.put(k, v);
map.put(k, v);
break;
case 4:
case 5:
log(op, k, v, "remove");
log(op, k, v, "m.remove({0})");
m.remove(k);
map.remove(k);
break;
case 6:
log(op, k, v, "store");
log(op, k, v, "s.store()");
s.store();
break;
case 7:
log(op, k, v, "compact");
log(op, k, v, "s.compact(90)");
s.compact(90);
break;
case 8:
log(op, k, v, "clear");
log(op, k, v, "m.clear()");
m.clear();
map.clear();
break;
case 9:
log(op, k, v, "commit");
log(op, k, v, "s.commit()");
s.commit();
break;
case 10:
log(op, k, v, "reopen");
log(op, k, v, "s.commit()");
s.commit();
log(op, k, v, "s.close()");
s.close();
s = new MVStore.Builder().fileName(fileName).
pageSplitSize(50).writeDelay(0).open();
log(op, k, v, "s = openStore(fileName)");
s = openStore(fileName);
log(op, k, v, "m = s.openMap(\"data\")");
m = s.openMap("data");
break;
case 11:
log(op, k, v, "compactMoveChunks");
log(op, k, v, "s.commit()");
s.commit();
log(op, k, v, "s.compactMoveChunks()");
s.compactMoveChunks();
break;
case 12:
log(op, k, v, "getKeyIndex");
log(op, k, v, "m.getKeyIndex({0})");
ArrayList<Integer> keyList = new ArrayList<Integer>(map.keySet());
int index = Collections.binarySearch(keyList, k, null);
int index2 = (int) m.getKeyIndex(k);
......@@ -167,6 +169,11 @@ public class TestRandomMapOps extends TestBase {
s.close();
}
private static MVStore openStore(String fileName) {
return new MVStore.Builder().fileName(fileName).
pageSplitSize(50).writeDelay(0).open();
}
private void assertEqualsMapValues(byte[] x, byte[] y) {
if (x == null || y == null) {
if (x != y) {
......@@ -186,7 +193,10 @@ public class TestRandomMapOps extends TestBase {
* @param msg the message
*/
private static void log(int op, int k, byte[] v, String msg) {
// System.out.println(op + ": " + msg + " key: " + k + " value: " + v);
// msg = MessageFormat.format(msg, k,
// v == null ? null : "new byte[" + v.length + "]");
// System.out.println(msg + "; // op " + op);
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论