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

A persistent multi-version map (work in progress) - open old versions

上级 f0d056d5
......@@ -43,6 +43,7 @@ public class TestBtreeMapStore extends TestBase {
}
public void test() {
testVersion();
testRtreeMany();
testRtree();
testRandomRtree();
......@@ -62,8 +63,59 @@ public class TestBtreeMapStore extends TestBase {
testSimple();
}
private void testVersion() {
String fileName = getBaseDir() + "/testVersion.h3";
FileUtils.delete(fileName);
BtreeMapStore s;
s = openStore(fileName);
BtreeMap<String, String> m;
s = openStore(fileName);
m = s.openMap("data", String.class, String.class);
long first = s.getCurrentVersion();
m.put("1", "Hello");
m.put("2", "World");
for (int i = 10; i < 20; i++) {
m.put("" + i, "data");
}
s.commit();
long old = s.getCurrentVersion();
m.put("1", "Hallo");
m.put("2", "Welt");
BtreeMap<String, String> mFirst;
mFirst = m.openVersion(first);
assertEquals(0, mFirst.size());
BtreeMap<String, String> mOld;
assertEquals("Hallo", m.get("1"));
assertEquals("Welt", m.get("2"));
mOld = m.openVersion(old);
assertEquals("Hello", mOld.get("1"));
assertEquals("World", mOld.get("2"));
assertTrue(mOld.isReadOnly());
s.getCurrentVersion();
s.setRetainChunk(0);
long old2 = s.store();
// TODO keep old version after storing
// assertEquals("Hello", mOld.get("1"));
// assertEquals("World", mOld.get("2"));
m.put("1", "Hi");
m.remove("2");
s.store();
s.close();
s = openStore(fileName);
m = s.openMap("data", String.class, String.class);
assertEquals("Hi", m.get("1"));
assertEquals(null, m.get("2"));
mOld = m.openVersion(old2);
assertEquals("Hallo", mOld.get("1"));
assertEquals("Welt", mOld.get("2"));
s.close();
}
private void testRtreeMany() {
String fileName = getBaseDir() + "/testMeta.h3";
String fileName = getBaseDir() + "/testRtree.h3";
FileUtils.delete(fileName);
BtreeMapStore s;
s = openStore(fileName);
......@@ -126,7 +178,7 @@ public class TestBtreeMapStore extends TestBase {
}
private void testRtree() {
String fileName = getBaseDir() + "/testMeta.h3";
String fileName = getBaseDir() + "/testRtree.h3";
FileUtils.delete(fileName);
BtreeMapStore s;
s = openStore(fileName);
......@@ -218,7 +270,7 @@ public class TestBtreeMapStore extends TestBase {
}
private void testRandomRtree() {
String fileName = getBaseDir() + "/testRandom.h3";
String fileName = getBaseDir() + "/testRtreeRandom.h3";
FileUtils.delete(fileName);
BtreeMapStore s = openStore(fileName);
RtreeMap<SpatialKey, String> m = s.openMap("data", "r", "s2", "");
......@@ -260,7 +312,7 @@ public class TestBtreeMapStore extends TestBase {
}
private void testCustomMapType() {
String fileName = getBaseDir() + "/testMeta.h3";
String fileName = getBaseDir() + "/testMapType.h3";
FileUtils.delete(fileName);
BtreeMapStore s;
s = openStore(fileName);
......@@ -274,7 +326,7 @@ public class TestBtreeMapStore extends TestBase {
}
private void testTruncateFile() {
String fileName = getBaseDir() + "/testMeta.h3";
String fileName = getBaseDir() + "/testTruncate.h3";
FileUtils.delete(fileName);
BtreeMapStore s;
BtreeMap<Integer, String> m;
......@@ -297,7 +349,7 @@ public class TestBtreeMapStore extends TestBase {
}
private void testFastDelete() {
String fileName = getBaseDir() + "/testMeta.h3";
String fileName = getBaseDir() + "/testFastDelete.h3";
FileUtils.delete(fileName);
BtreeMapStore s;
BtreeMap<Integer, String> m;
......@@ -325,7 +377,7 @@ public class TestBtreeMapStore extends TestBase {
}
private void testRollbackStored() {
String fileName = getBaseDir() + "/testMeta.h3";
String fileName = getBaseDir() + "/testRollback.h3";
FileUtils.delete(fileName);
BtreeMap<String, String> meta;
BtreeMapStore s = openStore(fileName);
......@@ -411,7 +463,7 @@ public class TestBtreeMapStore extends TestBase {
}
private void testRollbackInMemory() {
String fileName = getBaseDir() + "/testMeta.h3";
String fileName = getBaseDir() + "/testRollback.h3";
FileUtils.delete(fileName);
BtreeMapStore s = openStore(fileName);
assertEquals(1, s.getCurrentVersion());
......@@ -477,7 +529,7 @@ public class TestBtreeMapStore extends TestBase {
}
private void testLargeImport() {
String fileName = getBaseDir() + "/testCsv.h3";
String fileName = getBaseDir() + "/testImport.h3";
int len = 1000;
for (int j = 0; j < 5; j++) {
FileUtils.delete(fileName);
......
......@@ -19,20 +19,21 @@ import java.util.TreeMap;
*/
public class BtreeMap<K, V> {
protected final BtreeMapStore store;
protected Page root;
protected BtreeMapStore store;
private final int id;
private final String name;
private final DataType keyType;
private final DataType valueType;
private final long createVersion;
/**
* The map of old roots. The key is the new version, the value is the root
* before this version.
*/
private final TreeMap<Long, Page> oldRoots = new TreeMap<Long, Page>();
private boolean closed;
private boolean readOnly;
protected BtreeMap(BtreeMapStore store, int id, String name,
......@@ -301,14 +302,14 @@ public class BtreeMap<K, V> {
}
public void close() {
closed = true;
readOnly = true;
store = null;
oldRoots.clear();
root = null;
}
public boolean isClosed() {
return store == null;
return closed;
}
/**
......@@ -357,12 +358,12 @@ public class BtreeMap<K, V> {
Page c2 = remove(c, writeVersion, key);
if (c2 == null) {
// this child was deleted
if (p.getKeyCount() == 1) {
p = p.copyOnWrite(writeVersion);
p.remove(index);
if (p.getKeyCount() == 0) {
removePage(p);
p = p.getChildPage(0);
}
p = p.copyOnWrite(writeVersion);
p.remove(index);
} else if (oldSize != c2.getTotalSize()) {
p = p.copyOnWrite(writeVersion);
p.setChild(index, c2);
......@@ -527,7 +528,7 @@ public class BtreeMap<K, V> {
}
protected void checkOpen() {
if (store == null) {
if (closed) {
throw new IllegalStateException("This map is closed");
}
}
......@@ -545,7 +546,7 @@ public class BtreeMap<K, V> {
if (readOnly) {
buff.append(" readOnly");
}
if (store == null) {
if (closed) {
buff.append(" closed");
}
return buff.toString();
......@@ -576,4 +577,18 @@ public class BtreeMap<K, V> {
store.removePage(p.getPos());
}
public BtreeMap<K, V> openVersion(long version) {
if (version < createVersion) {
throw new IllegalArgumentException("Unknown version");
}
if (!oldRoots.containsKey(version)) {
return store.openMapVersion(version, name);
}
Page root = oldRoots.get(version);
BtreeMap<K, V> m = new BtreeMap<K, V>(store, id, name, keyType, valueType, createVersion);
m.readOnly = true;
m.root = root;
return m;
}
}
......@@ -40,7 +40,6 @@ blockSize=4096
TODO:
- ability to diff / merge versions
- map.getVersion and opening old maps read-only
- limited support for writing to old versions (branches)
- implement complete java.util.Map interface
- maybe rename to MVStore, MVMap, TestMVStore
......@@ -58,6 +57,9 @@ TODO:
- support database version / schema version
- implement more counted b-tree (skip, get positions)
- merge pages if small
- r-tree: add missing features (NN search for example)
- compression: maybe hash table reset speeds up compression
- avoid using java.util.Properties (it allocates quite a lot of memory)
*/
......@@ -142,6 +144,24 @@ public class BtreeMapStore {
return s;
}
@SuppressWarnings("unchecked")
<T extends BtreeMap<?, ?>> T openMapVersion(long version, String name) {
// TODO reduce copy & pasted source code
BtreeMap<String, String> oldMeta = getMetaMap(version);
String types = oldMeta.get("map." + name);
String[] idTypeList = StringUtils.arraySplit(types, '/', false);
int id = Integer.parseInt(idTypeList[0]);
long createVersion = Long.parseLong(idTypeList[1]);
String mapType = idTypeList[2];
String keyType = idTypeList[3];
String valueType = idTypeList[4];
String r = oldMeta.get("root." + id);
long root = r == null ? 0 : Long.parseLong(r);
BtreeMap<?, ?> m = buildMap(mapType, id, name, keyType, valueType, createVersion);
m.setRootPos(root);
return (T) m;
}
/**
* Open a map.
*
......@@ -177,19 +197,22 @@ public class BtreeMapStore {
String r = meta.get("root." + id);
root = r == null ? 0 : Long.parseLong(r);
}
DataType k = buildDataType(keyType);
DataType v = buildDataType(valueType);
if (mapType.equals("")) {
m = new BtreeMap<Object, Object>(this, id, name, k, v, createVersion);
} else {
m = getMapFactory().buildMap(mapType, this, id, name, k, v, createVersion);
}
m = buildMap(mapType, id, name, keyType, valueType, createVersion);
maps.put(name, m);
m.setRootPos(root);
}
return (T) m;
}
private BtreeMap<?, ?> buildMap(String mapType, int id, String name, String keyType, String valueType, long createVersion) {
DataType k = buildDataType(keyType);
DataType v = buildDataType(valueType);
if (mapType.equals("")) {
return new BtreeMap<Object, Object>(this, id, name, k, v, createVersion);
}
return getMapFactory().buildMap(mapType, this, id, name, k, v, createVersion);
}
/**
* Get the metadata map. It contains the following entries:
*
......@@ -207,6 +230,11 @@ public class BtreeMapStore {
private BtreeMap<String, String> getMetaMap(long version) {
Chunk c = getChunkForVersion(version);
if (c == null) {
throw new IllegalArgumentException("Unknown version: " + version);
}
// TODO avoid duplicate code
c = readChunkHeader(c.start);
BtreeMap<String, String> oldMeta = new BtreeMap<String, String>(this, 0, "old-meta", STRING_TYPE, STRING_TYPE, 0);
oldMeta.setRootPos(c.metaRootPos);
return oldMeta;
......@@ -603,7 +631,8 @@ public class BtreeMapStore {
*/
long registerTempPage(Page p) {
long pos = --tempPageId;
temp.put(pos, p);
// use -pos so the Long cache can be used
temp.put(-pos, p);
int index = (int) pos & (tempCache.length - 1);
tempCache[index] = p;
return pos;
......@@ -808,7 +837,7 @@ public class BtreeMapStore {
int index = (int) pos & (tempCache.length - 1);
Page p = tempCache[index];
if (p == null || p.getPos() != pos) {
p = temp.get(pos);
p = temp.get(-pos);
tempCache[index] = p;
}
return p;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论