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

A persistent tree map (work in progress).

上级 e392b3bf
......@@ -43,7 +43,8 @@ public class TestTreeMapStore extends TestBase {
FileUtils.delete(fileName);
BtreeMapStore s = BtreeMapStore.open(fileName);
BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
int count = 10000;
// int count = 0;
int count = 2000;
// Profiler p = new Profiler();
// p.startCollecting();
// long t = System.currentTimeMillis();
......@@ -71,6 +72,14 @@ public class TestTreeMapStore extends TestBase {
for (int i = 1; i < count; i++) {
assertEquals("hello " + i, m.get(i));
}
for (int i = 1; i < count; i++) {
m.remove(i);
}
s.store();
assertNull(m.get(0));
for (int i = 0; i < count; i++) {
assertNull(m.get(i));
}
s.close();
}
......@@ -95,6 +104,24 @@ public class TestTreeMapStore extends TestBase {
assertTrue("initial: " + initialLength + " len: " + len, len <= initialLength * 3);
}
}
long len = FileUtils.size(fileName);
// System.out.println("len0: " + len);
BtreeMapStore s = BtreeMapStore.open(fileName);
BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
for (int i = 0; i < 100; i++) {
m.remove(i);
}
s.store();
s.compact();
s.close();
len = FileUtils.size(fileName);
// System.out.println("len1: " + len);
s = BtreeMapStore.open(fileName);
m = s.openMap("data", Integer.class, String.class);
s.compact();
s.close();
len = FileUtils.size(fileName);
// System.out.println("len2: " + len);
}
private void testReuseSpace() {
......@@ -129,23 +156,43 @@ public class TestTreeMapStore extends TestBase {
BtreeMap<Integer, Integer> m = s.openMap("data", Integer.class, Integer.class);
TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
Random r = new Random(1);
for (int i = 0; i < 100; i++) {
int k = r.nextInt(20);
int operationCount = 1000;
int maxValue = 20;
for (int i = 0; i < operationCount; i++) {
int k = r.nextInt(maxValue);
int v = r.nextInt();
if (r.nextBoolean()) {
boolean compareAll;
switch (r.nextInt(3)) {
case 0:
m.put(k, v);
map.put(k, v);
} else {
compareAll = true;
break;
case 1:
m.remove(k);
map.remove(k);
compareAll = true;
break;
default:
Integer a = map.get(k);
Integer b = m.get(k);
if (a == null || b == null) {
assertTrue(a == b);
} else {
assertEquals(a.intValue(), b.intValue());
}
compareAll = false;
break;
}
Iterator<Integer> it = m.keyIterator(null);
Iterator<Integer> it2 = map.keySet().iterator();
while (it2.hasNext()) {
assertTrue(it.hasNext());
assertEquals(it2.next(), it.next());
if (compareAll) {
Iterator<Integer> it = m.keyIterator(null);
Iterator<Integer> it2 = map.keySet().iterator();
while (it2.hasNext()) {
assertTrue(it.hasNext());
assertEquals(it2.next(), it.next());
}
assertFalse(it.hasNext());
}
assertFalse(it.hasNext());
}
s.close();
}
......
......@@ -395,6 +395,27 @@ public class BtreeMap<K, V> {
return name;
}
/**
* Read a variable size long.
*
* @param buff the source buffer
* @return the value
*/
static long readVarLong(ByteBuffer buff) {
long x = buff.get();
if (x >= 0) {
return x;
}
x &= 0x7f;
for (int s = 7;; s += 7) {
long b = buff.get();
x |= (b & 0x7f) << s;
if (b >= 0) {
return x;
}
}
}
/**
* Read a variable size int.
*
......@@ -449,6 +470,23 @@ public class BtreeMap<K, V> {
return 5;
}
/**
* Get the length of the variable size long.
*
* @param x the value
* @return the length in bytes
*/
static int getVarLongLen(long x) {
int i = 1;
while (true) {
x >>>= 7;
if (x == 0) {
return i;
}
i++;
}
}
/**
* Write a variable size int.
*
......@@ -463,4 +501,18 @@ public class BtreeMap<K, V> {
buff.put((byte) x);
}
/**
* Write a variable size int.
*
* @param buff the target buffer
* @param x the value
*/
static void writeVarLong(ByteBuffer buff, long x) {
while ((x & ~0x7f) != 0) {
buff.put((byte) (0x80 | (x & 0x7f)));
x >>>= 7;
}
buff.put((byte) x);
}
}
......@@ -83,6 +83,10 @@ public class BtreeMapStore {
private int loadCount;
// TODO support quota (per map, per storage)
// TODO support r-tree
// TODO support triggers and events (possibly on a different layer)
private BtreeMapStore(String fileName) {
this.fileName = fileName;
}
......@@ -260,7 +264,10 @@ public class BtreeMapStore {
for (BtreeMap<?, ?> m : mapsChanged.values()) {
meta.put("map." + m.getName(), String.valueOf(Long.MAX_VALUE) +
"," + m.getKeyType().getName() + "," + m.getValueType().getName());
lenEstimate += m.getRoot().lengthIncludingTempChildren();
Page p = m.getRoot();
if (p != null) {
lenEstimate += p.lengthIncludingTempChildren();
}
}
int blockId = ++lastBlockId;
Block b = new Block(blockId);
......@@ -311,7 +318,10 @@ public class BtreeMapStore {
meta.put("block." + b.id, b.toString());
int count = 0;
for (BtreeMap<?, ?> m : mapsChanged.values()) {
count += m.getRoot().countTemp();
Page p = m.getRoot();
if (p != null) {
count += p.countTemp();
}
}
count += meta.getRoot().countTemp();
......@@ -329,7 +339,10 @@ public class BtreeMapStore {
buff.putInt(b.id);
buff.putInt(metaRootOffset);
for (BtreeMap<?, ?> m : mapsChanged.values()) {
m.getRoot().storeTemp(buff);
Page p = m.getRoot();
if (p != null) {
p.storeTemp(buff);
}
}
meta.getRoot().storeTemp(buff);
if (buff.hasRemaining()) {
......@@ -507,25 +520,29 @@ public class BtreeMapStore {
Class<?> vt = BtreeMap.getClass(d[2]);
BtreeMap<?, ?> oldData = BtreeMap.open(this, "old-" + k, kt, vt);
long oldDataRoot = Long.parseLong(d[0]);
oldData.setRoot(oldDataRoot);
@SuppressWarnings("unchecked")
BtreeMap<Object, Object> data = (BtreeMap<Object, Object>) maps.get(k);
Iterator<?> dataIt = oldData.keyIterator(null);
while (dataIt.hasNext()) {
Object o = dataIt.next();
Page p = data.getPage(o);
if (p == null) {
// was removed later - ignore
} else if (p.getId() < 0) {
// temporarily changed - ok
// TODO move old data if changed temporarily?
} else {
Block b = getBlock(p.getId());
if (old.contains(b)) {
log(" move key:" + o + " block:" + b.id);
Object value = data.get(o);
data.remove(o);
data.put(o, value);
if (oldDataRoot == 0) {
// no rows
} else {
oldData.setRoot(oldDataRoot);
@SuppressWarnings("unchecked")
BtreeMap<Object, Object> data = (BtreeMap<Object, Object>) maps.get(k);
Iterator<?> dataIt = oldData.keyIterator(null);
while (dataIt.hasNext()) {
Object o = dataIt.next();
Page p = data.getPage(o);
if (p == null) {
// was removed later - ignore
} else if (p.getId() < 0) {
// temporarily changed - ok
// TODO move old data if changed temporarily?
} else {
Block b = getBlock(p.getId());
if (old.contains(b)) {
log(" move key:" + o + " block:" + b.id);
Object value = data.get(o);
data.remove(o);
data.put(o, value);
}
}
}
}
......@@ -594,7 +611,7 @@ public class BtreeMapStore {
/**
* A block of data.
*/
static class Block implements Comparable<Block> {
static class Block {
public int collectPriority;
int id;
long start;
......@@ -632,10 +649,6 @@ public class BtreeMapStore {
return entryCount == 0 ? 0 : 100 * liveCount / entryCount;
}
public int compareTo(Block o) {
return start == o.start ? 0 : start < o.start ? -1 : 1;
}
public int hashCode() {
return id;
}
......
......@@ -63,6 +63,8 @@ class Page {
}
private Page copyOnWrite() {
// TODO avoid creating objects (arrays) that are then not used
// possibly add shortcut for copy with add / copy with remove
long t = map.getTransaction();
if (transaction == t) {
return this;
......@@ -103,15 +105,6 @@ class Page {
return id;
}
/**
* Set the page id.
*
* @param id the new id
*/
void setId(long id) {
this.id = id;
}
/**
* Get the value for the given key, or null if not found.
*
......@@ -259,7 +252,7 @@ class Page {
}
}
private int size() {
private int keyCount() {
return keys.length;
}
......@@ -325,9 +318,9 @@ class Page {
parent.children[parentIndex] = p.id;
}
if (!p.isLeaf()) {
if (p.size() >= MAX_SIZE) {
if (p.keyCount() >= MAX_SIZE) {
// TODO almost duplicate code
int pos = p.size() / 2;
int pos = p.keyCount() / 2;
Object k = p.keys[pos];
Page split = p.splitNode(pos);
if (parent == null) {
......@@ -354,8 +347,8 @@ class Page {
}
index = -index - 1;
p.insert(index, key, value, 0);
if (p.size() >= MAX_SIZE) {
int pos = p.size() / 2;
if (p.keyCount() >= MAX_SIZE) {
int pos = p.keyCount() / 2;
Object k = p.keys[pos];
Page split = p.splitLeaf(pos);
if (parent == null) {
......@@ -387,43 +380,39 @@ class Page {
* @return the new root node
*/
static Page remove(Page p, Object key) {
// TODO avoid separate lookup
if (p.find(key) == null) {
return p;
}
p = p.copyOnWrite();
Page top = p;
Page parent = null;
int parentIndex = 0;
while (true) {
if (parent != null) {
parent.children[parentIndex] = p.id;
}
int index = p.findKey(key);
if (p.isLeaf()) {
if (index >= 0) {
p.remove(index);
} else {
// not found?
throw new RuntimeException("Not found: " + key);
}
if (p.size() == 0) {
if (parent != null) {
parent.remove(parentIndex);
// TODO recursive, or on the way down
}
int index = p.findKey(key);
if (p.isLeaf()) {
if (index >= 0) {
if (p.keyCount() == 1) {
return null;
}
break;
p = p.copyOnWrite();
p.remove(index);
} else {
// not found
}
if (index < 0) {
index = -index - 1;
return p;
}
// node
if (index < 0) {
index = -index - 1;
}
Page c = p.map.readPage(p.children[index]);
Page c2 = remove(c, key);
if (c2 == c) {
// not found
} else if (c2 == null) {
// child was deleted
p = p.copyOnWrite();
p.remove(index);
if (p.keyCount() == 0) {
p = c2;
}
parent = p;
parentIndex = index;
p = p.map.readPage(p.children[index]);
} else {
p = p.copyOnWrite();
p.children[index] = c2.id;
}
return top;
return p;
}
private void insert(int index, Object key, Object value, long child) {
......@@ -461,16 +450,6 @@ class Page {
}
}
// int x = findKey(key);
// if (x >= 0) {
// return values[x];
// }
// x = -x - 1;
// Page p = map.readPage(children[x]);
// return p.find(key);
// return null;
// }
private void read(ByteBuffer buff) {
boolean node = buff.get() == 1;
if (node) {
......@@ -502,9 +481,9 @@ class Page {
void write(ByteBuffer buff) {
if (children != null) {
buff.put((byte) 1);
int size = children.length;
BtreeMap.writeVarInt(buff, size);
for (int i = 0; i < size; i++) {
int len = children.length;
BtreeMap.writeVarInt(buff, len);
for (int i = 0; i < len; i++) {
long c = map.readPage(children[i]).storedId;
buff.putLong(c);
if (i < keys.length) {
......@@ -513,9 +492,9 @@ class Page {
}
} else {
buff.put((byte) 0);
int size = keys.length;
BtreeMap.writeVarInt(buff, size);
for (int i = 0; i < size; i++) {
int len = keys.length;
BtreeMap.writeVarInt(buff, len);
for (int i = 0; i < len; i++) {
map.getKeyType().write(buff, keys[i]);
map.getValueType().write(buff, values[i]);
}
......@@ -528,17 +507,17 @@ class Page {
* @return the length
*/
int lengthIncludingTempChildren() {
int len = length();
int byteCount = length();
if (children != null) {
int size = children.length;
for (int i = 0; i < size; i++) {
int len = children.length;
for (int i = 0; i < len; i++) {
long c = children[i];
if (c < 0) {
len += map.readPage(c).lengthIncludingTempChildren();
byteCount += map.readPage(c).lengthIncludingTempChildren();
}
}
}
return len;
return byteCount;
}
/**
......@@ -551,8 +530,8 @@ class Page {
this.storedId = pageId;
pageId += length();
if (children != null) {
int size = children.length;
for (int i = 0; i < size; i++) {
int len = children.length;
for (int i = 0; i < len; i++) {
long c = children[i];
if (c < 0) {
pageId = map.readPage(c).updatePageIds(pageId);
......@@ -571,8 +550,8 @@ class Page {
long storeTemp(ByteBuffer buff) {
write(buff);
if (children != null) {
int size = children.length;
for (int i = 0; i < size; i++) {
int len = children.length;
for (int i = 0; i < len; i++) {
long c = children[i];
if (c < 0) {
children[i] = map.readPage(c).storeTemp(buff);
......@@ -608,25 +587,25 @@ class Page {
* @return the length
*/
int length() {
int len = 1;
int byteCount = 1;
if (children != null) {
int size = children.length;
len += BtreeMap.getVarIntLen(size);
for (int i = 0; i < size; i++) {
len += 8;
int len = children.length;
byteCount += BtreeMap.getVarIntLen(len);
for (int i = 0; i < len; i++) {
byteCount += 8;
if (i < keys.length) {
len += map.getKeyType().length(keys[i]);
byteCount += map.getKeyType().length(keys[i]);
}
}
} else {
int size = keys.length;
len += BtreeMap.getVarIntLen(size);
for (int i = 0; i < size; i++) {
len += map.getKeyType().length(keys[i]);
len += map.getValueType().length(values[i]);
int len = keys.length;
byteCount += BtreeMap.getVarIntLen(len);
for (int i = 0; i < len; i++) {
byteCount += map.getKeyType().length(keys[i]);
byteCount += map.getValueType().length(values[i]);
}
}
return len;
return byteCount;
}
private static void copyWithGap(Object src, Object dst, int oldSize, int gapIndex) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论