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

MVStore: bugfixes

上级 86af1cc6
...@@ -84,14 +84,13 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -84,14 +84,13 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* *
* @param p the page * @param p the page
* @param writeVersion the write version * @param writeVersion the write version
* @param removeOld whether the old page should be marked as deleted
* @return a page with the given write version * @return a page with the given write version
*/ */
protected Page copyOnWrite(Page p, long writeVersion, boolean removeOld) { protected Page copyOnWrite(Page p, long writeVersion) {
if (p.getVersion() == writeVersion) { if (p.getVersion() == writeVersion) {
return p; return p;
} }
return p.copy(writeVersion, removeOld); return p.copy(writeVersion);
} }
/** /**
...@@ -108,7 +107,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -108,7 +107,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
beforeWrite(); beforeWrite();
try { try {
long v = writeVersion; long v = writeVersion;
Page p = copyOnWrite(root, v, true); Page p = copyOnWrite(root, v);
p = splitRootIfNeeded(p, v); p = splitRootIfNeeded(p, v);
Object result = put(p, v, key, value); Object result = put(p, v, key, value);
newRoot(p); newRoot(p);
...@@ -167,7 +166,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -167,7 +166,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
} else { } else {
index++; index++;
} }
Page c = copyOnWrite(p.getChildPage(index), writeVersion, true); Page c = copyOnWrite(p.getChildPage(index), writeVersion);
if (c.getMemory() > store.getPageSplitSize() && c.getKeyCount() > 1) { if (c.getMemory() > store.getPageSplitSize() && c.getKeyCount() > 1) {
// split on the way down // split on the way down
int at = c.getKeyCount() / 2; int at = c.getKeyCount() / 2;
...@@ -552,7 +551,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -552,7 +551,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
beforeWrite(); beforeWrite();
try { try {
long v = writeVersion; long v = writeVersion;
Page p = copyOnWrite(root, v, true); Page p = copyOnWrite(root, v);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
V result = (V) remove(p, v, key); V result = (V) remove(p, v, key);
newRoot(p); newRoot(p);
...@@ -671,13 +670,14 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -671,13 +670,14 @@ public class MVMap<K, V> extends AbstractMap<K, V>
index++; index++;
} }
Page cOld = p.getChildPage(index); Page cOld = p.getChildPage(index);
Page c = copyOnWrite(cOld, writeVersion, false); Page c = copyOnWrite(cOld, writeVersion);
result = remove(c, writeVersion, key); result = remove(c, writeVersion, key);
if (result == null) { if (result == null || c.getTotalCount() != 0) {
return null; // no change, or
} // there are more nodes
cOld.removePage(); p.setChild(index, c);
if (c.getTotalCount() == 0) { p.setCounts(index, c);
} else {
// this child was deleted // this child was deleted
if (p.getKeyCount() == 0) { if (p.getKeyCount() == 0) {
p.setChild(index, c); p.setChild(index, c);
...@@ -686,9 +686,6 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -686,9 +686,6 @@ public class MVMap<K, V> extends AbstractMap<K, V>
} else { } else {
p.remove(index); p.remove(index);
} }
} else {
p.setChild(index, c);
p.setCounts(index, c);
} }
return result; return result;
} }
...@@ -733,7 +730,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -733,7 +730,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* *
* @return the key type * @return the key type
*/ */
protected DataType getKeyType() { public DataType getKeyType() {
return keyType; return keyType;
} }
...@@ -742,7 +739,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -742,7 +739,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* *
* @return the value type * @return the value type
*/ */
protected DataType getValueType() { public DataType getValueType() {
return valueType; return valueType;
} }
...@@ -1130,7 +1127,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1130,7 +1127,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* @param name the map name (or null) * @param name the map name (or null)
* @return the string * @return the string
*/ */
public String asString(String name) { String asString(String name) {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
if (name != null) { if (name != null) {
DataUtils.appendMap(buff, "name", name); DataUtils.appendMap(buff, "name", name);
......
...@@ -29,8 +29,8 @@ public class MVMapConcurrent<K, V> extends MVMap<K, V> { ...@@ -29,8 +29,8 @@ public class MVMapConcurrent<K, V> extends MVMap<K, V> {
} }
@Override @Override
protected Page copyOnWrite(Page p, long writeVersion, boolean removeOld) { protected Page copyOnWrite(Page p, long writeVersion) {
return p.copy(writeVersion, removeOld); return p.copy(writeVersion);
} }
@Override @Override
...@@ -48,7 +48,7 @@ public class MVMapConcurrent<K, V> extends MVMap<K, V> { ...@@ -48,7 +48,7 @@ public class MVMapConcurrent<K, V> extends MVMap<K, V> {
get(key); get(key);
long v = writeVersion; long v = writeVersion;
synchronized (this) { synchronized (this) {
Page p = copyOnWrite(root, v, true); Page p = copyOnWrite(root, v);
p = splitRootIfNeeded(p, v); p = splitRootIfNeeded(p, v);
V result = (V) put(p, v, key, value); V result = (V) put(p, v, key, value);
newRoot(p); newRoot(p);
...@@ -75,7 +75,7 @@ public class MVMapConcurrent<K, V> extends MVMap<K, V> { ...@@ -75,7 +75,7 @@ public class MVMapConcurrent<K, V> extends MVMap<K, V> {
} }
long v = writeVersion; long v = writeVersion;
synchronized (this) { synchronized (this) {
Page p = copyOnWrite(root, v, true); Page p = copyOnWrite(root, v);
result = (V) remove(p, v, key); result = (V) remove(p, v, key);
newRoot(p); newRoot(p);
} }
......
...@@ -484,6 +484,16 @@ public class MVStore { ...@@ -484,6 +484,16 @@ public class MVStore {
meta.remove("root." + id); meta.remove("root." + id);
maps.remove(id); maps.remove(id);
} }
/**
* Check whether a given map exists.
*
* @param name the map name
* @return true if it exists
*/
public boolean hasMap(String name) {
return meta.containsKey("name." + name);
}
/** /**
* Mark a map as changed (containing unsaved changes). * Mark a map as changed (containing unsaved changes).
......
...@@ -267,19 +267,16 @@ public class Page { ...@@ -267,19 +267,16 @@ public class Page {
* Create a copy of this page. * Create a copy of this page.
* *
* @param version the new version * @param version the new version
* @param removeOld whether the old page should be marked as deleted
* @return a page with the given version * @return a page with the given version
*/ */
public Page copy(long version, boolean removeOld) { public Page copy(long version) {
if (removeOld) {
int todoRemoveAtCaller;
removePage();
}
Page newPage = create(map, version, Page newPage = create(map, version,
keyCount, keys, values, children, childrenPages, keyCount, keys, values, children, childrenPages,
counts, totalCount, counts, totalCount,
SHARED_KEYS | SHARED_VALUES | SHARED_CHILDREN | SHARED_COUNTS, SHARED_KEYS | SHARED_VALUES | SHARED_CHILDREN | SHARED_COUNTS,
memory); memory);
// mark the old as deleted
removePage();
newPage.cachedCompare = cachedCompare; newPage.cachedCompare = cachedCompare;
return newPage; return newPage;
} }
......
...@@ -5,10 +5,13 @@ ...@@ -5,10 +5,13 @@
*/ */
package org.h2.test.store; package org.h2.test.store;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random; import java.util.Random;
import java.util.TreeMap; import java.util.TreeMap;
import org.h2.mvstore.MVMap; import org.h2.mvstore.MVMap;
import org.h2.mvstore.MVMapConcurrent;
import org.h2.mvstore.MVStore; import org.h2.mvstore.MVStore;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase; import org.h2.test.TestBase;
...@@ -19,6 +22,7 @@ import org.h2.test.TestBase; ...@@ -19,6 +22,7 @@ import org.h2.test.TestBase;
public class TestRandomMapOps extends TestBase { public class TestRandomMapOps extends TestBase {
private String fileName; private String fileName;
private boolean concurrent;
private int seed; private int seed;
private int op; private int op;
...@@ -33,17 +37,18 @@ public class TestRandomMapOps extends TestBase { ...@@ -33,17 +37,18 @@ public class TestRandomMapOps extends TestBase {
@Override @Override
public void test() throws Exception { public void test() throws Exception {
test("memFS:randomOps.h3"); concurrent = false;
int todoTestConcurrentMap; testMap("memFS:randomOps.h3");
int todoTestMVRTreeMap; concurrent = true;
testMap("memFS:randomOps.h3");
} }
public void test(String fileName) { public void testMap(String fileName) {
this.fileName = fileName; this.fileName = fileName;
int best = Integer.MAX_VALUE; int best = Integer.MAX_VALUE;
int bestSeed = 0; int bestSeed = 0;
Throwable failException = null; Throwable failException = null;
for (seed = 0; seed < 1000; seed++) { for (seed = 0; seed < 100; seed++) {
FileUtils.delete(fileName); FileUtils.delete(fileName);
Throwable ex = null; Throwable ex = null;
try { try {
...@@ -70,20 +75,23 @@ public class TestRandomMapOps extends TestBase { ...@@ -70,20 +75,23 @@ public class TestRandomMapOps extends TestBase {
private void testCase() throws Exception { private void testCase() throws Exception {
FileUtils.delete(fileName); FileUtils.delete(fileName);
MVStore s; MVStore s;
MVMap<Integer, byte[]> m;
s = new MVStore.Builder().fileName(fileName). s = new MVStore.Builder().fileName(fileName).
pageSplitSize(50).writeDelay(0).open(); pageSplitSize(50).writeDelay(0).open();
m = s.openMap("data"); 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); Random r = new Random(seed);
op = 0; op = 0;
int size = getSize(10, 100); int size = getSize(100, 1000);
TreeMap<Integer, byte[]> map = new TreeMap<Integer, byte[]>(); TreeMap<Integer, byte[]> map = new TreeMap<Integer, byte[]>();
for (; op < size; op++) { for (; op < size; op++) {
int k = r.nextInt(100); int k = r.nextInt(100);
byte[] v = new byte[r.nextInt(10) * 10]; byte[] v = new byte[r.nextInt(10) * 10];
int type = r.nextInt(11); int type = r.nextInt(13);
switch (type) { switch (type) {
case 0: case 0:
case 1: case 1:
...@@ -105,7 +113,7 @@ public class TestRandomMapOps extends TestBase { ...@@ -105,7 +113,7 @@ public class TestRandomMapOps extends TestBase {
break; break;
case 7: case 7:
log(op, k, v, "compact"); log(op, k, v, "compact");
s.compact(80); s.compact(90);
break; break;
case 8: case 8:
log(op, k, v, "clear"); log(op, k, v, "clear");
...@@ -124,6 +132,22 @@ public class TestRandomMapOps extends TestBase { ...@@ -124,6 +132,22 @@ public class TestRandomMapOps extends TestBase {
pageSplitSize(50).writeDelay(0).open(); pageSplitSize(50).writeDelay(0).open();
m = s.openMap("data"); m = s.openMap("data");
break; break;
case 11:
log(op, k, v, "compactMoveChunks");
s.commit();
s.compactMoveChunks();
break;
case 12:
log(op, k, v, "getKeyIndex");
ArrayList<Integer> keyList = new ArrayList<Integer>(map.keySet());
int index = Collections.binarySearch(keyList, k, null);
int index2 = (int) m.getKeyIndex(k);
assertEquals(index, index2);
if (index >= 0) {
int k2 = m.getKey(index);
assertEquals(k2, k);
}
break;
} }
assertEqualsMapValues(map.get(k), m.get(k)); assertEqualsMapValues(map.get(k), m.get(k));
assertEquals(map.ceilingKey(k), m.ceilingKey(k)); assertEquals(map.ceilingKey(k), m.ceilingKey(k));
...@@ -131,8 +155,10 @@ public class TestRandomMapOps extends TestBase { ...@@ -131,8 +155,10 @@ public class TestRandomMapOps extends TestBase {
assertEquals(map.higherKey(k), m.higherKey(k)); assertEquals(map.higherKey(k), m.higherKey(k));
assertEquals(map.lowerKey(k), m.lowerKey(k)); assertEquals(map.lowerKey(k), m.lowerKey(k));
assertEquals(map.isEmpty(), m.isEmpty()); assertEquals(map.isEmpty(), m.isEmpty());
if (map.size() != m.size()) { assertEquals(map.size(), m.size());
assertEquals(map.size(), m.size()); if (!map.isEmpty()) {
assertEquals(map.firstKey(), m.firstKey());
assertEquals(map.lastKey(), m.lastKey());
} }
} }
s.store(); s.store();
...@@ -149,6 +175,14 @@ public class TestRandomMapOps extends TestBase { ...@@ -149,6 +175,14 @@ public class TestRandomMapOps extends TestBase {
} }
} }
/**
* Log the operation
*
* @param op the operation id
* @param k the key
* @param v the value
* @param msg the message
*/
private static void log(int op, int k, byte[] v, String msg) { private static void log(int op, int k, byte[] v, String msg) {
// System.out.println(op + ": " + msg + " key: " + k + " value: " + v); // System.out.println(op + ": " + msg + " key: " + k + " value: " + v);
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论