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

A persistent multi-version map (work in progress)

上级 1217a01e
...@@ -19,11 +19,6 @@ import java.util.TreeMap; ...@@ -19,11 +19,6 @@ import java.util.TreeMap;
*/ */
public class BtreeMap<K, V> { public class BtreeMap<K, V> {
protected static final IllegalArgumentException KEY_NOT_FOUND = new IllegalArgumentException(
"Key not found");
protected static final IllegalArgumentException KEY_ALREADY_EXISTS = new IllegalArgumentException(
"Key already exists");
protected Page root; protected Page root;
protected BtreeMapStore store; protected BtreeMapStore store;
...@@ -59,76 +54,32 @@ public class BtreeMap<K, V> { ...@@ -59,76 +54,32 @@ public class BtreeMap<K, V> {
public void put(K key, V data) { public void put(K key, V data) {
checkWrite(); checkWrite();
Page oldRoot = root; Page oldRoot = root;
if (containsKey(key)) { root = put(oldRoot, store.getCurrentVersion(), key, data, store.getMaxPageSize());
root = set(root, store.getCurrentVersion(), key, data);
} else {
root = add(root, store.getCurrentVersion(), key, data);
}
markChanged(oldRoot); markChanged(oldRoot);
} }
/** /**
* Update a value for an existing key. * Add or update a key-value pair.
*
* @param map the map
* @param p the page (may not be null)
* @param writeVersion the write version
* @param key the key
* @param value the value
* @return the root page
* @throws InvalidArgumentException if this key does not exist (without
* stack trace)
*/
protected Page set(Page p, long writeVersion, Object key, Object value) {
if (p == null) {
throw KEY_NOT_FOUND;
}
int index = p.binarySearch(key);
if (p.isLeaf()) {
if (index < 0) {
throw KEY_NOT_FOUND;
}
p = p.copyOnWrite(writeVersion);
p.setValue(index, value);
return p;
}
// it is a node
if (index < 0) {
index = -index - 1;
} else {
index++;
}
Page c = p.getChildPage(index);
Page c2 = set(c, writeVersion, key, value);
if (c != c2) {
p = p.copyOnWrite(writeVersion);
p.setChild(index, c2);
}
return p;
}
/**
* Add a new key-value pair.
* *
* @param map the map * @param map the map
* @param p the page (may be null) * @param p the page (may be null)
* @param writeVersion the write version * @param writeVersion the write version
* @param key the key * @param key the key
* @param value the value * @param value the value (may not be null)
* @param maxPageSize the maximum page size
* @return the root page * @return the root page
* @throws InvalidArgumentException if this key already exists (without
* stack trace)
*/ */
protected Page add(Page p, long writeVersion, Object key, Object value) { protected Page put(Page p, long writeVersion, Object key, Object value, int maxPageSize) {
if (p == null) { if (p == null) {
Object[] keys = { key }; Object[] keys = { key };
Object[] values = { value }; Object[] values = { value };
p = Page.create(this, writeVersion, keys, values, null, null, 1); p = Page.create(this, writeVersion, 1,
keys, values, null, null, 1, 0);
return p; return p;
} }
if (p.getKeyCount() >= store.getMaxPageSize()) { if (p.getKeyCount() > maxPageSize) {
// only possible if this is the root, // only possible if this is the root, else we would have split earlier
// otherwise we would have split earlier // (this requires maxPageSize is fixed)
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
int at = p.getKeyCount() / 2; int at = p.getKeyCount() / 2;
long totalSize = p.getTotalSize(); long totalSize = p.getTotalSize();
...@@ -137,17 +88,18 @@ public class BtreeMap<K, V> { ...@@ -137,17 +88,18 @@ public class BtreeMap<K, V> {
Object[] keys = { k }; Object[] keys = { k };
long[] children = { p.getPos(), split.getPos() }; long[] children = { p.getPos(), split.getPos() };
long[] childrenSize = { p.getTotalSize(), split.getTotalSize() }; long[] childrenSize = { p.getTotalSize(), split.getTotalSize() };
p = Page.create(this, writeVersion, keys, null, children, childrenSize, p = Page.create(this, writeVersion, 1,
totalSize); keys, null, children, childrenSize, totalSize, 0);
// now p is a node; insert continues // now p is a node; insert continues
} else if (p.isLeaf()) { } else if (p.isLeaf()) {
int index = p.binarySearch(key); int index = p.binarySearch(key);
if (index >= 0) {
throw KEY_ALREADY_EXISTS;
}
index = -index - 1;
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
p.insert(index, key, value, 0, 0); if (index < 0) {
index = -index - 1;
p.insertLeaf(index, key, value);
} else {
p.setValue(index, value);
}
return p; return p;
} }
// p is a node // p is a node
...@@ -158,7 +110,7 @@ public class BtreeMap<K, V> { ...@@ -158,7 +110,7 @@ public class BtreeMap<K, V> {
index++; index++;
} }
Page c = p.getChildPage(index); Page c = p.getChildPage(index);
if (c.getKeyCount() >= store.getMaxPageSize()) { if (c.getKeyCount() >= maxPageSize) {
// split on the way down // split on the way down
c = c.copyOnWrite(writeVersion); c = c.copyOnWrite(writeVersion);
int at = c.getKeyCount() / 2; int at = c.getKeyCount() / 2;
...@@ -166,14 +118,16 @@ public class BtreeMap<K, V> { ...@@ -166,14 +118,16 @@ public class BtreeMap<K, V> {
Page split = c.split(at); Page split = c.split(at);
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
p.setChild(index, split); p.setChild(index, split);
p.insert(index, k, null, c.getPos(), c.getTotalSize()); p.insertNode(index, k, c.getPos(), c.getTotalSize());
// now we are not sure where to add // now we are not sure where to add
return add(p, writeVersion, key, value); return put(p, writeVersion, key, value, maxPageSize);
} }
Page c2 = add(c, writeVersion, key, value); long oldSize = c.getTotalSize();
Page c2 = put(c, writeVersion, key, value, maxPageSize);
if (c != c2 || oldSize != c2.getTotalSize()) {
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
// the child might be the same, but not the size
p.setChild(index, c2); p.setChild(index, c2);
}
return p; return p;
} }
...@@ -364,26 +318,22 @@ public class BtreeMap<K, V> { ...@@ -364,26 +318,22 @@ public class BtreeMap<K, V> {
*/ */
public void remove(K key) { public void remove(K key) {
checkWrite(); checkWrite();
if (containsKey(key)) {
Page oldRoot = root; Page oldRoot = root;
root = removeExisting(root, store.getCurrentVersion(), key); if (oldRoot != null) {
root = remove(oldRoot, store.getCurrentVersion(), key);
markChanged(oldRoot); markChanged(oldRoot);
} }
} }
/** /**
* Remove an existing key-value pair. * Remove a key-value pair.
* *
* @param p the page (may not be null) * @param p the page (may not be null)
* @param writeVersion the write version * @param writeVersion the write version
* @param key the key * @param key the key
* @return the new root page (null if empty) * @return the new root page (null if empty)
* @throws InvalidArgumentException if not found (without stack trace)
*/ */
protected Page removeExisting(Page p, long writeVersion, Object key) { protected Page remove(Page p, long writeVersion, Object key) {
if (p == null) {
throw KEY_NOT_FOUND;
}
int index = p.binarySearch(key); int index = p.binarySearch(key);
if (p.isLeaf()) { if (p.isLeaf()) {
if (index >= 0) { if (index >= 0) {
...@@ -393,8 +343,6 @@ public class BtreeMap<K, V> { ...@@ -393,8 +343,6 @@ public class BtreeMap<K, V> {
} }
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
p.remove(index); p.remove(index);
} else {
throw KEY_NOT_FOUND;
} }
return p; return p;
} }
...@@ -405,16 +353,18 @@ public class BtreeMap<K, V> { ...@@ -405,16 +353,18 @@ public class BtreeMap<K, V> {
index++; index++;
} }
Page c = p.getChildPage(index); Page c = p.getChildPage(index);
Page c2 = removeExisting(c, writeVersion, key); long oldSize = c.getTotalSize();
p = p.copyOnWrite(writeVersion); Page c2 = remove(c, writeVersion, key);
if (c2 == null) { if (c2 == null) {
// this child was deleted // this child was deleted
p = p.copyOnWrite(writeVersion);
p.remove(index); p.remove(index);
if (p.getKeyCount() == 0) { if (p.getKeyCount() == 0) {
removePage(p); removePage(p);
p = p.getChildPage(0); p = p.getChildPage(0);
} }
} else { } else if (oldSize != c2.getTotalSize()) {
p = p.copyOnWrite(writeVersion);
p.setChild(index, c2); p.setChild(index, c2);
} }
return p; return p;
......
...@@ -11,6 +11,7 @@ import java.io.StringReader; ...@@ -11,6 +11,7 @@ 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.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet; import java.util.BitSet;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
...@@ -56,6 +57,7 @@ TODO: ...@@ -56,6 +57,7 @@ TODO:
- compare with newest version of IndexedDb - compare with newest version of IndexedDb
- support database version / schema version - support database version / schema version
- implement more counted b-tree (skip, get positions) - implement more counted b-tree (skip, get positions)
- merge pages if small
*/ */
...@@ -64,7 +66,7 @@ TODO: ...@@ -64,7 +66,7 @@ TODO:
*/ */
public class BtreeMapStore { public class BtreeMapStore {
public static final boolean ASSERT = true; public static final boolean ASSERT = false;
private static final StringType STRING_TYPE = new StringType(); private static final StringType STRING_TYPE = new StringType();
...@@ -82,6 +84,7 @@ public class BtreeMapStore { ...@@ -82,6 +84,7 @@ public class BtreeMapStore {
private int tempPageId; private int tempPageId;
private Map<Long, Page> cache = CacheLIRS.newInstance(readCacheSize, 2048); private Map<Long, Page> cache = CacheLIRS.newInstance(readCacheSize, 2048);
private HashMap<Long, Page> temp = New.hashMap(); private HashMap<Long, Page> temp = New.hashMap();
private Page[] tempCache = new Page[64];
private int lastChunkId; private int lastChunkId;
private HashMap<Integer, Chunk> chunks = New.hashMap(); private HashMap<Integer, Chunk> chunks = New.hashMap();
...@@ -367,6 +370,7 @@ public class BtreeMapStore { ...@@ -367,6 +370,7 @@ public class BtreeMapStore {
m.close(); m.close();
} }
temp.clear(); temp.clear();
Arrays.fill(tempCache, null);
meta = null; meta = null;
compressor = null; compressor = null;
chunks.clear(); chunks.clear();
...@@ -598,9 +602,11 @@ public class BtreeMapStore { ...@@ -598,9 +602,11 @@ public class BtreeMapStore {
* @return the page id * @return the page id
*/ */
long registerTempPage(Page p) { long registerTempPage(Page p) {
long id = --tempPageId; long pos = --tempPageId;
temp.put(id, p); temp.put(pos, p);
return id; int index = (int) pos & (tempCache.length - 1);
tempCache[index] = p;
return pos;
} }
/** /**
...@@ -799,7 +805,13 @@ public class BtreeMapStore { ...@@ -799,7 +805,13 @@ public class BtreeMapStore {
*/ */
Page readPage(BtreeMap<?, ?> map, long pos) { Page readPage(BtreeMap<?, ?> map, long pos) {
if (pos < 0) { if (pos < 0) {
return temp.get(pos); int index = (int) pos & (tempCache.length - 1);
Page p = tempCache[index];
if (p == null || p.getPos() != pos) {
p = temp.get(pos);
tempCache[index] = p;
}
return p;
} }
Page p = cache.get(pos); Page p = cache.get(pos);
if (p == null) { if (p == null) {
...@@ -993,6 +1005,7 @@ public class BtreeMapStore { ...@@ -993,6 +1005,7 @@ public class BtreeMapStore {
} }
mapsChanged.clear(); mapsChanged.clear();
temp.clear(); temp.clear();
Arrays.fill(tempCache, null);
tempPageId = 0; tempPageId = 0;
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论