提交 8efabfdd authored 作者: Thomas Mueller's avatar Thomas Mueller

A persistent tree map (work in progress)

上级 7efe8087
...@@ -29,17 +29,45 @@ public class TestTreeMapStore extends TestBase { ...@@ -29,17 +29,45 @@ public class TestTreeMapStore extends TestBase {
} }
public void test() throws Exception { public void test() throws Exception {
// testReuseSpace();
testRandom(); testRandom();
testKeyValueClasses(); testKeyValueClasses();
testIterate(); testIterate();
testSimple(); testSimple();
} }
private void testReuseSpace() {
String fileName = getBaseDir() + "/data.h3";
FileUtils.delete(fileName);
for (int j = 0; j < 10; j++) {
System.out.println("@@@open");
TreeMapStore s = TreeMapStore.open(fileName);
StoredMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
System.out.println(s);
System.out.println("get0: " + m.get(0));
System.out.println("@@@add");
for (int i = 0; i < 10; i++) {
m.put(i, "Hello");
}
s.store();
System.out.println(s);
System.out.println("@@@remove");
for (int i = 0; i < 10; i++) {
m.remove(i);
}
s.store();
System.out.println(s);
System.out.println("@@@close");
s.close();
}
if(true)System.exit(0);
}
private void testRandom() { private void testRandom() {
String fileName = getBaseDir() + "/data.h3"; String fileName = getBaseDir() + "/data.h3";
FileUtils.delete(fileName); FileUtils.delete(fileName);
TreeMapStore s = TreeMapStore.open(fileName); TreeMapStore s = TreeMapStore.open(fileName);
StoredMap<Integer, Integer> m = s.openMap("intString", Integer.class, Integer.class); StoredMap<Integer, Integer> m = s.openMap("data", Integer.class, Integer.class);
TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>(); TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
Random r = new Random(1); Random r = new Random(1);
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {
......
...@@ -9,9 +9,12 @@ package org.h2.dev.store; ...@@ -9,9 +9,12 @@ package org.h2.dev.store;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.util.BitSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties; import java.util.Properties;
import java.util.TreeSet; import java.util.TreeSet;
import org.h2.store.fs.FilePath; import org.h2.store.fs.FilePath;
...@@ -24,19 +27,16 @@ file format: ...@@ -24,19 +27,16 @@ file format:
header header
header header
[ transaction log | data ] * [ chunk ] *
header: header:
# H3 store # # H3 store #
pageSize=4096 pageSize=4096
r=1 r=1
data: chunk:
'd' [length] root ... 'd' [length] root ...
transaction log:
't' [length] ...
todo: todo:
- garbage collection - garbage collection
...@@ -75,7 +75,7 @@ public class TreeMapStore { ...@@ -75,7 +75,7 @@ public class TreeMapStore {
private long transaction; private long transaction;
private int tempNodeId; private int tempNodeId;
private long storePos; private int nextBlockId;
private int loadCount; private int loadCount;
...@@ -111,24 +111,36 @@ public class TreeMapStore { ...@@ -111,24 +111,36 @@ public class TreeMapStore {
void open() { void open() {
meta = StoredMap.open(this, "meta", String.class, String.class); meta = StoredMap.open(this, "meta", String.class, String.class);
new File(fileName).getParentFile().mkdirs(); new File(fileName).getParentFile().mkdirs();
try { try {
file = FilePathCache.wrap(FilePath.get(fileName).open("rw")); file = FilePathCache.wrap(FilePath.get(fileName).open("rw"));
if (file.size() == 0) { if (file.size() == 0) {
writeHeader(); writeHeader();
storePos = pageSize * 2;
} else { } else {
readHeader(); readHeader();
if (rootPos > 0) { if (rootPos > 0) {
meta.setRoot(rootPos); meta.setRoot(rootPos);
} }
readMeta();
} }
} catch (Exception e) { } catch (Exception e) {
throw convert(e); throw convert(e);
} }
} }
private void readMeta() {
Iterator<String> it = meta.keyIterator("block.");
while (it.hasNext()) {
String s = it.next();
if (!s.startsWith("block.")) {
break;
}
Block b = Block.fromString(meta.get(s));
nextBlockId = Math.max(b.id + 1, nextBlockId);
blocks.add(b);
}
}
private void writeHeader() { private void writeHeader() {
try { try {
ByteBuffer header = ByteBuffer.wrap(( ByteBuffer header = ByteBuffer.wrap((
...@@ -136,8 +148,7 @@ public class TreeMapStore { ...@@ -136,8 +148,7 @@ public class TreeMapStore {
"read-version: 1\n" + "read-version: 1\n" +
"write-version: 1\n" + "write-version: 1\n" +
"root: " + rootPos + "\n" + "root: " + rootPos + "\n" +
"transaction: " + transaction + "\n" + "transaction: " + transaction + "\n").getBytes());
"storePos: " + storePos + "\n").getBytes());
file.position(0); file.position(0);
file.write(header); file.write(header);
file.position(pageSize); file.position(pageSize);
...@@ -156,7 +167,6 @@ public class TreeMapStore { ...@@ -156,7 +167,6 @@ public class TreeMapStore {
Properties prop = new Properties(); Properties prop = new Properties();
prop.load(new ByteArrayInputStream(header)); prop.load(new ByteArrayInputStream(header));
rootPos = Long.parseLong(prop.get("root").toString()); rootPos = Long.parseLong(prop.get("root").toString());
storePos = Long.parseLong(prop.get("storePos").toString());
transaction = Long.parseLong(prop.get("transaction").toString()); transaction = Long.parseLong(prop.get("transaction").toString());
} catch (Exception e) { } catch (Exception e) {
throw convert(e); throw convert(e);
...@@ -164,7 +174,7 @@ public class TreeMapStore { ...@@ -164,7 +174,7 @@ public class TreeMapStore {
} }
public String toString() { public String toString() {
return "cache size: " + cache.size() + " loadCount: " + this.loadCount + " " + blocks; return "cache size: " + cache.size() + " loadCount: " + loadCount + "\n" + blocks.toString().replace('\n', ' ');
} }
private static RuntimeException convert(Exception e) { private static RuntimeException convert(Exception e) {
...@@ -183,7 +193,22 @@ public class TreeMapStore { ...@@ -183,7 +193,22 @@ public class TreeMapStore {
} }
} }
private int length(Node n) {
int len = 0;
if (n != null) {
len += n.length();
if (n.getLeftId() < 0) {
len += length(n.getLeft());
}
if (n.getRightId() < 0) {
len += length(n.getRight());
}
}
return len;
}
private long updateId(Node n, long offset) { private long updateId(Node n, long offset) {
if (n != null) {
n.setId(offset); n.setId(offset);
cache.put(offset, n); cache.put(offset, n);
offset += n.length(); offset += n.length();
...@@ -193,10 +218,14 @@ public class TreeMapStore { ...@@ -193,10 +218,14 @@ public class TreeMapStore {
if (n.getRightId() < 0) { if (n.getRightId() < 0) {
offset = updateId(n.getRight(), offset); offset = updateId(n.getRight(), offset);
} }
}
return offset; return offset;
} }
private int store(ByteBuffer buff, Node n) { private void store(ByteBuffer buff, Node n) {
if (n == null) {
return;
}
Node left = n.getLeftId() < 0 ? n.getLeft() : null; Node left = n.getLeftId() < 0 ? n.getLeft() : null;
if (left != null) { if (left != null) {
n.setLeftId(left.getId()); n.setLeftId(left.getId());
...@@ -205,15 +234,13 @@ public class TreeMapStore { ...@@ -205,15 +234,13 @@ public class TreeMapStore {
if (right != null) { if (right != null) {
n.setRightId(right.getId()); n.setRightId(right.getId());
} }
int count = 1;
n.write(buff); n.write(buff);
if (left != null) { if (left != null) {
count += store(buff, left); store(buff, left);
} }
if (right != null) { if (right != null) {
count += store(buff, right); store(buff, right);
} }
return count;
} }
public void store() { public void store() {
...@@ -222,28 +249,55 @@ public class TreeMapStore { ...@@ -222,28 +249,55 @@ public class TreeMapStore {
return; return;
} }
commit(); commit();
Block b = new Block(storePos);
b.transaction = transaction; // the length estimate is not correct,
// as we don't know the exact positions and entry counts
int lenEstimate = 1 + 8;
for (StoredMap<?, ?> m : mapsChanged.values()) {
meta.put("map." + m.getName(), String.valueOf(Long.MAX_VALUE));
lenEstimate += length(m.getRoot());
}
Block b = new Block(nextBlockId++);
b.start = Long.MAX_VALUE;
b.entryCount = Integer.MAX_VALUE;
b.liveCount = Integer.MAX_VALUE;
blocks.add(b);
for (Block x : blocks) {
if (x.liveCount == 0) {
meta.remove("block." + x.id);
} else {
meta.put("block." + x.id, x.toString());
}
}
lenEstimate += length(meta.getRoot());
b.length = lenEstimate;
long storePos = allocate(lenEstimate);
int test;
// System.out.println("use " + storePos + ".." + (storePos + lenEstimate));
long end = storePos + 1 + 8; long end = storePos + 1 + 8;
for (StoredMap<?, ?> m : mapsChanged.values()) { for (StoredMap<?, ?> m : mapsChanged.values()) {
meta.put("map." + m.getName(), String.valueOf(end)); meta.put("map." + m.getName(), String.valueOf(end));
end = updateId(m.getRoot(), end); end = updateId(m.getRoot(), end);
} }
long metaPos = end; long metaPos = end;
b.start = storePos;
b.entryCount = tempNodeId;
b.liveCount = b.entryCount;
meta.put("block." + b.id, b.toString());
end = updateId(meta.getRoot(), end); end = updateId(meta.getRoot(), end);
ByteBuffer buff = ByteBuffer.allocate((int) (end - storePos)); ByteBuffer buff = ByteBuffer.allocate((int) (end - storePos));
buff.put((byte) 'd'); buff.put((byte) 'd');
buff.putLong(metaPos - storePos); buff.putLong(metaPos - storePos);
int entryCount = 0;
for (StoredMap<?, ?> m : mapsChanged.values()) { for (StoredMap<?, ?> m : mapsChanged.values()) {
entryCount += store(buff, m.getRoot()); store(buff, m.getRoot());
} }
entryCount += store(buff, meta.getRoot()); store(buff, meta.getRoot());
b.entryCount = entryCount;
b.liveCount = b.entryCount;
b.length = buff.limit();
blocks.add(b);
if (buff.hasRemaining()) { if (buff.hasRemaining()) {
throw new RuntimeException("remaining: " + buff.remaining()); throw new RuntimeException("remaining: " + buff.remaining());
} }
...@@ -255,12 +309,41 @@ public class TreeMapStore { ...@@ -255,12 +309,41 @@ public class TreeMapStore {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
rootPos = meta.getRoot().getId(); rootPos = meta.getRoot().getId();
storePos = end;
writeHeader(); writeHeader();
tempNodeId = 0; tempNodeId = 0;
mapsChanged.clear(); mapsChanged.clear();
} }
private long allocate(long length) {
BitSet set = new BitSet();
set.set(0);
set.set(1);
for (Block b : blocks) {
if (b.start == Long.MAX_VALUE) {
continue;
}
int first = (int) (b.start / pageSize);
int last = (int) ((b.start + b.length) / pageSize);
set.set(first, last +1);
}
int required = (int) (length / pageSize) + 1;
for (int i = 0; i < set.size(); i++) {
if (!set.get(i)) {
boolean ok = true;
for (int j = 1; j <= required; j++) {
if (set.get(i + j)) {
ok = false;
break;
}
}
if (ok) {
return i * pageSize;
}
}
}
return set.size() * pageSize;
}
long getTransaction() { long getTransaction() {
return transaction; return transaction;
} }
...@@ -353,21 +436,39 @@ public class TreeMapStore { ...@@ -353,21 +436,39 @@ public class TreeMapStore {
} }
private Block getBlock(long pos) { private Block getBlock(long pos) {
return blocks.lower(new Block(pos)); Block b = new Block(0);
b.start = pos;
return blocks.headSet(b).last();
} }
/** /**
* A block of data. * A block of data.
*/ */
static class Block implements Comparable<Block> { static class Block implements Comparable<Block> {
long transaction; int id;
long start; long start;
long length; long length;
int entryCount; int entryCount;
int liveCount; int liveCount;
int referencesToOthers; // int outRevCount;
Block(int id) {
this.id = id;
}
Block(long start) { public static Block fromString(String s) {
this.start = start; Block b = new Block(0);
Properties prop = new Properties();
try {
prop.load(new StringReader(s));
b.id = Integer.parseInt(prop.get("id").toString());
b.start = Long.parseLong(prop.get("start").toString());
b.length = Long.parseLong(prop.get("length").toString());
b.entryCount = Integer.parseInt(prop.get("entryCount").toString());
b.liveCount = Integer.parseInt(prop.get("liveCount").toString());
return b;
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
public int compareTo(Block o) { public int compareTo(Block o) {
...@@ -375,8 +476,12 @@ public class TreeMapStore { ...@@ -375,8 +476,12 @@ public class TreeMapStore {
} }
public String toString() { public String toString() {
return "[" + start + "-" + (start + length - 1) + " c:" + entryCount + " l:" return
+ liveCount + " " + (100 * liveCount / entryCount) + "%]"; "id:" + id + "\n" +
"start:" + start + "\n" +
"length:" + length + "\n" +
"entryCount:" + entryCount + "\n" +
"liveCount:" + liveCount + "\n";
} }
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论