提交 606aa497 authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore: maps can now be renamed.

上级 8d221f87
...@@ -62,7 +62,8 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -62,7 +62,8 @@ public class MVMap<K, V> extends AbstractMap<K, V>
public void open(MVStore store, HashMap<String, String> config) { public void open(MVStore store, HashMap<String, String> config) {
this.store = store; this.store = store;
this.id = Integer.parseInt(config.get("id")); this.id = Integer.parseInt(config.get("id"));
this.createVersion = Long.parseLong(config.get("createVersion")); String x = config.get("createVersion");
this.createVersion = x == null ? 0 : Long.parseLong(x);
} }
/** /**
...@@ -799,7 +800,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -799,7 +800,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
checkWrite(); checkWrite();
removeUnusedOldVersions(); removeUnusedOldVersions();
if (version <= createVersion) { if (version <= createVersion) {
removeMap(); // the map is removed later
} else if (root.getVersion() >= version) { } else if (root.getVersion() >= version) {
// iterating in descending order - // iterating in descending order -
// this is not terribly efficient if there are many versions // this is not terribly efficient if there are many versions
...@@ -997,24 +998,38 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -997,24 +998,38 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* @return the map type * @return the map type
*/ */
public String getType() { public String getType() {
return "btree"; return null;
} }
/** /**
* Get the map metadata as a string. * Get the map metadata as a string.
* *
* @param name the map name (or null)
* @return the string * @return the string
*/ */
public String asString() { public String asString(String name) {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
DataUtils.appendMap(buff, "id", id); if (name != null) {
DataUtils.appendMap(buff, "type", getType()); DataUtils.appendMap(buff, "name", name);
}
if (createVersion != 0) {
DataUtils.appendMap(buff, "createVersion", createVersion); DataUtils.appendMap(buff, "createVersion", createVersion);
}
String type = getType();
if (type != null) {
DataUtils.appendMap(buff, "type", type);
}
if (keyType != null) { if (keyType != null) {
DataUtils.appendMap(buff, "key", keyType.asString()); String k = keyType.asString();
if (k.length() > 0) {
DataUtils.appendMap(buff, "key", k);
}
} }
if (valueType != null) { if (valueType != null) {
DataUtils.appendMap(buff, "value", valueType.asString()); String v = valueType.asString();
if (v.length() > 0) {
DataUtils.appendMap(buff, "value", v);
}
} }
return buff.toString(); return buff.toString();
} }
...@@ -1024,13 +1039,13 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1024,13 +1039,13 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* *
* @param newMapName the name name * @param newMapName the name name
*/ */
public void rename(String newMapName) { public void renameMap(String newMapName) {
checkWrite(); checkWrite();
store.renameMap(this, newMapName); store.renameMap(this, newMapName);
} }
public String toString() { public String toString() {
return asString(); return asString(null);
} }
} }
...@@ -44,9 +44,7 @@ H:3,... ...@@ -44,9 +44,7 @@ H:3,...
TODO: TODO:
- after rollback: is a regular save ok?
- cache: change API to better match guava / Android - cache: change API to better match guava / Android
- rename a map
- MVStore: improved API thanks to Simo Tripodi - MVStore: improved API thanks to Simo Tripodi
- implement table engine for H2 - implement table engine for H2
- automated 'kill process' and 'power failure' test - automated 'kill process' and 'power failure' test
...@@ -154,8 +152,7 @@ public class MVStore { ...@@ -154,8 +152,7 @@ public class MVStore {
private final HashMap<Long, HashMap<Integer, Chunk>> freedChunks = New.hashMap(); private final HashMap<Long, HashMap<Integer, Chunk>> freedChunks = New.hashMap();
private MVMap<String, String> meta; private MVMap<String, String> meta;
private final HashMap<String, MVMap<?, ?>> maps = New.hashMap(); private final HashMap<Integer, MVMap<?, ?>> maps = New.hashMap();
private final HashMap<Integer, String> mapIdName = New.hashMap();
/** /**
* The set of maps with potentially unsaved changes. * The set of maps with potentially unsaved changes.
...@@ -291,34 +288,37 @@ public class MVStore { ...@@ -291,34 +288,37 @@ public class MVStore {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends MVMap<K, V>, K, V> T openMap(String name, T template) { public <T extends MVMap<K, V>, K, V> T openMap(String name, T template) {
checkOpen(); checkOpen();
MVMap<K, V> m = (MVMap<K, V>) maps.get(name); MVMap<K, V> m;
String x = meta.get("name." + name);
int id;
long root;
HashMap<String, String> c;
if (x != null) {
id = Integer.parseInt(x);
m = (MVMap<K, V>) maps.get(id);
if (m != null) { if (m != null) {
return (T) m; return (T) m;
} }
m = template; m = template;
String config = meta.get("map." + name); String config = meta.get("map." + x);
long root; c = DataUtils.parseMap(config);
int id; c.put("id", x);
HashMap<String, String> c; m.open(this, c);
if (config == null) { String r = meta.get("root." + id);
root = r == null ? 0 : Long.parseLong(r);
} else {
c = New.hashMap(); c = New.hashMap();
id = ++lastMapId; id = ++lastMapId;
c.put("id", Integer.toString(id)); c.put("id", Integer.toString(id));
c.put("name", name);
c.put("createVersion", Long.toString(currentVersion)); c.put("createVersion", Long.toString(currentVersion));
m = template;
m.open(this, c); m.open(this, c);
meta.put("map." + name, m.asString()); meta.put("map." + id, m.asString(name));
meta.put("name." + name, Integer.toString(id));
root = 0; root = 0;
} else {
c = DataUtils.parseMap(config);
id = Integer.parseInt(c.get("id"));
String r = meta.get("root." + id);
root = r == null ? 0 : Long.parseLong(r);
} }
m.open(this, c);
m.setRootPos(root, -1); m.setRootPos(root, -1);
mapIdName.put(id, name); maps.put(id, m);
maps.put(name, m);
return (T) m; return (T) m;
} }
...@@ -370,12 +370,12 @@ public class MVStore { ...@@ -370,12 +370,12 @@ public class MVStore {
* @param id the map id * @param id the map id
*/ */
void removeMap(int id) { void removeMap(int id) {
String name = mapIdName.get(id); String name = getMapName(id);
meta.remove("map." + name); meta.remove("map." + id);
meta.remove("name." + name);
meta.remove("root." + id); meta.remove("root." + id);
mapsChanged.remove(id); mapsChanged.remove(id);
mapIdName.remove(id); maps.remove(id);
maps.remove(name);
} }
private DataType getDataType(Class<?> clazz) { private DataType getDataType(Class<?> clazz) {
...@@ -402,7 +402,6 @@ public class MVStore { ...@@ -402,7 +402,6 @@ public class MVStore {
meta = new MVMap<String, String>(StringDataType.INSTANCE, StringDataType.INSTANCE); meta = new MVMap<String, String>(StringDataType.INSTANCE, StringDataType.INSTANCE);
HashMap<String, String> c = New.hashMap(); HashMap<String, String> c = New.hashMap();
c.put("id", "0"); c.put("id", "0");
c.put("name", "meta");
c.put("createVersion", Long.toString(currentVersion)); c.put("createVersion", Long.toString(currentVersion));
meta.open(this, c); meta.open(this, c);
if (fileName == null) { if (fileName == null) {
...@@ -572,7 +571,6 @@ public class MVStore { ...@@ -572,7 +571,6 @@ public class MVStore {
chunks.clear(); chunks.clear();
cache.clear(); cache.clear();
maps.clear(); maps.clear();
mapIdName.clear();
mapsChanged.clear(); mapsChanged.clear();
} catch (Exception e) { } catch (Exception e) {
throw DataUtils.illegalStateException("Closing failed for file " + fileName, e); throw DataUtils.illegalStateException("Closing failed for file " + fileName, e);
...@@ -1004,7 +1002,7 @@ public class MVStore { ...@@ -1004,7 +1002,7 @@ public class MVStore {
if (mapId == 0) { if (mapId == 0) {
return meta; return meta;
} }
return maps.get(mapIdName.get(mapId)); return maps.get(mapId);
} }
/** /**
...@@ -1250,7 +1248,6 @@ public class MVStore { ...@@ -1250,7 +1248,6 @@ public class MVStore {
if (!isKnownVersion(version)) { if (!isKnownVersion(version)) {
throw DataUtils.illegalArgumentException("Unknown version: " + version); throw DataUtils.illegalArgumentException("Unknown version: " + version);
} }
// TODO could remove newer temporary pages on rollback
for (MVMap<?, ?> m : mapsChanged.values()) { for (MVMap<?, ?> m : mapsChanged.values()) {
m.rollbackTo(version); m.rollbackTo(version);
} }
...@@ -1286,12 +1283,11 @@ public class MVStore { ...@@ -1286,12 +1283,11 @@ public class MVStore {
readMeta(); readMeta();
} }
} }
int todoRollbackMapNames; for (MVMap<?, ?> m : New.arrayList(maps.values())) {
for (MVMap<?, ?> m : maps.values()) {
int id = m.getId(); int id = m.getId();
if (m.getCreateVersion() >= version) { if (m.getCreateVersion() >= version) {
m.close(); m.close();
removeMap(id); maps.remove(id);
} else { } else {
if (loadFromFile) { if (loadFromFile) {
String r = meta.get("root." + id); String r = meta.get("root." + id);
...@@ -1389,20 +1385,20 @@ public class MVStore { ...@@ -1389,20 +1385,20 @@ public class MVStore {
if (map.getName().equals(newName)) { if (map.getName().equals(newName)) {
return; return;
} }
if (meta.containsKey("map." + newName)) { if (meta.containsKey("name." + newName)) {
throw DataUtils.illegalArgumentException("A map named " + newName + " already exists"); throw DataUtils.illegalArgumentException("A map named " + newName + " already exists");
} }
int id = map.getId(); int id = map.getId();
String oldName = mapIdName.remove(id); String oldName = getMapName(id);
maps.remove(oldName); meta.remove("map." + id);
String value = meta.remove("map." + oldName); meta.remove("name." + oldName);
meta.put("map." + newName, value); meta.put("map." + id, map.asString(newName));
maps.put(newName, map); meta.put("name." + newName, Integer.toString(id));
mapIdName.put(id, newName);
} }
String getMapName(int id) { String getMapName(int id) {
return mapIdName.get(id); String m = meta.get("map." + id);
return DataUtils.parseMap(m).get("name");
} }
} }
...@@ -55,7 +55,7 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -55,7 +55,7 @@ public class MVPrimaryIndex extends BaseIndex {
public void renameTable(String newName) { public void renameTable(String newName) {
rename(newName + "_DATA"); rename(newName + "_DATA");
map.rename(newName + "_DATA_" + getId()); map.renameMap(newName + "_DATA_" + getId());
} }
public String getCreateSQL() { public String getCreateSQL() {
......
...@@ -73,7 +73,7 @@ public class MVSecondaryIndex extends BaseIndex { ...@@ -73,7 +73,7 @@ public class MVSecondaryIndex extends BaseIndex {
} }
public void rename(String newName) { public void rename(String newName) {
map.rename(newName + "_" + getId()); map.renameMap(newName + "_" + getId());
super.rename(newName); super.rename(newName);
} }
......
...@@ -42,6 +42,7 @@ public class TestMVStore extends TestBase { ...@@ -42,6 +42,7 @@ public class TestMVStore extends TestBase {
public void test() throws Exception { public void test() throws Exception {
FileUtils.deleteRecursive(getBaseDir(), true); FileUtils.deleteRecursive(getBaseDir(), true);
testRenameMapRollback();
testCustomMapType(); testCustomMapType();
testCacheSize(); testCacheSize();
testConcurrentOpen(); testConcurrentOpen();
...@@ -73,11 +74,24 @@ public class TestMVStore extends TestBase { ...@@ -73,11 +74,24 @@ public class TestMVStore extends TestBase {
testSimple(); testSimple();
} }
private void testRenameMapRollback() {
MVStore s = openStore(null);
MVMap<Integer, Integer> map;
map = s.openMap("hello");
map.put(1, 10);
long old = s.incrementVersion();
map.renameMap("world");
map.put(2, 20);
assertEquals("world", map.getName());
s.rollbackTo(old);
assertEquals("hello", map.getName());
s.close();
}
private void testCustomMapType() { private void testCustomMapType() {
String fileName = getBaseDir() + "/testMapType.h3"; String fileName = getBaseDir() + "/testMapType.h3";
FileUtils.delete(fileName); FileUtils.delete(fileName);
MVStore s; MVStore s = openStore(fileName);
s = openStore(fileName);
SequenceMap seq = new SequenceMap(); SequenceMap seq = new SequenceMap();
seq = s.openMap("data", seq); seq = s.openMap("data", seq);
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
...@@ -634,7 +648,7 @@ public class TestMVStore extends TestBase { ...@@ -634,7 +648,7 @@ public class TestMVStore extends TestBase {
assertTrue(s.hasUnsavedChanges()); assertTrue(s.hasUnsavedChanges());
s.rollbackTo(v2); s.rollbackTo(v2);
assertFalse(s.hasUnsavedChanges()); assertFalse(s.hasUnsavedChanges());
assertNull(meta.get("map.data1")); assertNull(meta.get("name.data1"));
assertNull(m0.get("1")); assertNull(m0.get("1"));
assertEquals("Hello", m.get("1")); assertEquals("Hello", m.get("1"));
assertEquals(2, s.store()); assertEquals(2, s.store());
...@@ -643,9 +657,9 @@ public class TestMVStore extends TestBase { ...@@ -643,9 +657,9 @@ public class TestMVStore extends TestBase {
s = openStore(fileName); s = openStore(fileName);
assertEquals(2, s.getCurrentVersion()); assertEquals(2, s.getCurrentVersion());
meta = s.getMetaMap(); meta = s.getMetaMap();
assertTrue(meta.get("map.data") != null); assertTrue(meta.get("name.data") != null);
assertTrue(meta.get("map.data0") != null); assertTrue(meta.get("name.data0") != null);
assertNull(meta.get("map.data1")); assertNull(meta.get("name.data1"));
m = s.openMap("data", String.class, String.class); m = s.openMap("data", String.class, String.class);
m0 = s.openMap("data0", String.class, String.class); m0 = s.openMap("data0", String.class, String.class);
assertNull(m0.get("1")); assertNull(m0.get("1"));
...@@ -735,17 +749,16 @@ public class TestMVStore extends TestBase { ...@@ -735,17 +749,16 @@ public class TestMVStore extends TestBase {
assertTrue(m.containsKey("chunk.1")); assertTrue(m.containsKey("chunk.1"));
assertFalse(m.containsKey("chunk.2")); assertFalse(m.containsKey("chunk.2"));
assertEquals("id:1,type:btree,createVersion:0,key:,value:", String id = s.getMetaMap().get("name.data");
m.get("map.data")); assertEquals("name:data", m.get("map." + id));
assertTrue(m.containsKey("chunk.1")); assertTrue(m.containsKey("chunk.1"));
assertEquals("Hello", data.put("1", "Hallo")); assertEquals("Hello", data.put("1", "Hallo"));
s.store(); s.store();
assertEquals("id:1,type:btree,createVersion:0,key:,value:", assertEquals("name:data", m.get("map." + id));
m.get("map.data"));
assertTrue(m.get("root.1").length() > 0); assertTrue(m.get("root.1").length() > 0);
assertTrue(m.containsKey("chunk.1")); assertTrue(m.containsKey("chunk.1"));
assertEquals("id:1,length:271,maxLength:288,maxLengthLive:0," + assertEquals("id:1,length:246,maxLength:224,maxLengthLive:0," +
"metaRoot:274877910924,pageCount:2," + "metaRoot:274877910922,pageCount:2," +
"start:8192,time:0,version:1", m.get("chunk.1")); "start:8192,time:0,version:1", m.get("chunk.1"));
assertTrue(m.containsKey("chunk.2")); assertTrue(m.containsKey("chunk.2"));
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论