提交 3791fdca authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore: improved robustness

上级 fabc1c3a
...@@ -476,6 +476,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -476,6 +476,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* Remove all entries, and close the map. * Remove all entries, and close the map.
*/ */
public void removeMap() { public void removeMap() {
checkOpen();
if (this != store.getMetaMap()) { if (this != store.getMetaMap()) {
checkWrite(); checkWrite();
root.removeAllRecursive(); root.removeAllRecursive();
...@@ -779,11 +780,11 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -779,11 +780,11 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* *
* @return the name * @return the name
*/ */
String getName() { public String getName() {
return name; return name;
} }
MVStore getStore() { public MVStore getStore() {
return store; return store;
} }
......
...@@ -78,8 +78,8 @@ TODO: ...@@ -78,8 +78,8 @@ TODO:
- auto-save temporary data if it uses too much memory, - auto-save temporary data if it uses too much memory,
-- but revert it on startup if needed. -- but revert it on startup if needed.
- map and chunk metadata: do not store default values - map and chunk metadata: do not store default values
- support maps without values (non-unique indexes), - support maps without values (just existence of the key)
- and maps without keys (counted b-tree) - support maps without keys (counted b-tree features)
- use a small object cache (StringCache) - use a small object cache (StringCache)
- dump values - dump values
- support Object[] and similar serialization by default - support Object[] and similar serialization by default
...@@ -95,6 +95,11 @@ TODO: ...@@ -95,6 +95,11 @@ TODO:
- close the file on out of memory or disk write error (out of disk space or so) - close the file on out of memory or disk write error (out of disk space or so)
- implement a shareded map (in one store, multiple stores) - implement a shareded map (in one store, multiple stores)
-- to support concurrent updates and writes, and very large maps -- to support concurrent updates and writes, and very large maps
- implement an off-heap file system
- optimize API for Java 7 (diamond operator)
- use new MVStore.Builder().open();
- see Google Guice: Generic Type
- JAXB (java xml binding) new TypeReference<String, String>(){}
*/ */
...@@ -155,7 +160,7 @@ public class MVStore { ...@@ -155,7 +160,7 @@ public class MVStore {
private ByteBuffer writeBuffer; private ByteBuffer writeBuffer;
private final boolean readOnly; private boolean readOnly;
private int lastMapId; private int lastMapId;
...@@ -177,6 +182,8 @@ public class MVStore { ...@@ -177,6 +182,8 @@ public class MVStore {
private long creationTime; private long creationTime;
private int retentionTime = 45; private int retentionTime = 45;
private boolean closed;
MVStore(HashMap<String, Object> config) { MVStore(HashMap<String, Object> config) {
this.config = config; this.config = config;
this.fileName = (String) config.get("fileName"); this.fileName = (String) config.get("fileName");
...@@ -257,6 +264,7 @@ public class MVStore { ...@@ -257,6 +264,7 @@ public class MVStore {
* @return the map * @return the map
*/ */
public <K, V> MVMap<K, V> openMap(String name, Class<K> keyClass, Class<V> valueClass) { public <K, V> MVMap<K, V> openMap(String name, Class<K> keyClass, Class<V> valueClass) {
checkOpen();
DataType keyType = getDataType(keyClass); DataType keyType = getDataType(keyClass);
DataType valueType = getDataType(valueClass); DataType valueType = getDataType(valueClass);
MVMap<K, V> m = new MVMap<K, V>(keyType, valueType); MVMap<K, V> m = new MVMap<K, V>(keyType, valueType);
...@@ -276,6 +284,7 @@ public class MVStore { ...@@ -276,6 +284,7 @@ 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();
MVMap<K, V> m = (MVMap<K, V>) maps.get(name); MVMap<K, V> m = (MVMap<K, V>) maps.get(name);
if (m != null) { if (m != null) {
return (T) m; return (T) m;
...@@ -319,6 +328,7 @@ public class MVStore { ...@@ -319,6 +328,7 @@ public class MVStore {
* @return the metadata map * @return the metadata map
*/ */
public MVMap<String, String> getMetaMap() { public MVMap<String, String> getMetaMap() {
checkOpen();
return meta; return meta;
} }
...@@ -351,6 +361,8 @@ public class MVStore { ...@@ -351,6 +361,8 @@ public class MVStore {
*/ */
void removeMap(String name) { void removeMap(String name) {
MVMap<?, ?> map = maps.remove(name); MVMap<?, ?> map = maps.remove(name);
meta.remove("map." + name);
meta.remove("root." + map.getId());
mapsChanged.remove(map.getId()); mapsChanged.remove(map.getId());
} }
...@@ -387,7 +399,11 @@ public class MVStore { ...@@ -387,7 +399,11 @@ public class MVStore {
FileUtils.createDirectories(FileUtils.getParent(fileName)); FileUtils.createDirectories(FileUtils.getParent(fileName));
try { try {
log("file open"); log("file open");
file = FilePathCache.wrap(FilePath.get(fileName).open("rw")); FilePath f = FilePath.get(fileName);
if (f.exists() && !f.canWrite()) {
readOnly = true;
}
file = FilePathCache.wrap(f.open(readOnly ? "r" : "rw"));
if (readOnly) { if (readOnly) {
fileLock = file.tryLock(0, Long.MAX_VALUE, true); fileLock = file.tryLock(0, Long.MAX_VALUE, true);
if (fileLock == null) { if (fileLock == null) {
...@@ -527,6 +543,7 @@ public class MVStore { ...@@ -527,6 +543,7 @@ public class MVStore {
* Close the file. Uncommitted changes are ignored, and all open maps are closed. * Close the file. Uncommitted changes are ignored, and all open maps are closed.
*/ */
public void close() { public void close() {
closed = true;
if (file != null) { if (file != null) {
try { try {
shrinkFileIfPossible(0); shrinkFileIfPossible(0);
...@@ -579,6 +596,7 @@ public class MVStore { ...@@ -579,6 +596,7 @@ public class MVStore {
* @return the new version (incremented if there were changes) * @return the new version (incremented if there were changes)
*/ */
public long store() { public long store() {
checkOpen();
if (!hasUnsavedChanges()) { if (!hasUnsavedChanges()) {
return currentVersion; return currentVersion;
} }
...@@ -729,6 +747,7 @@ public class MVStore { ...@@ -729,6 +747,7 @@ public class MVStore {
} }
private void applyFreedChunks() { private void applyFreedChunks() {
// TODO support concurrent operations
for (HashMap<Integer, Chunk> freed : freedChunks.values()) { for (HashMap<Integer, Chunk> freed : freedChunks.values()) {
for (Chunk f : freed.values()) { for (Chunk f : freed.values()) {
Chunk c = chunks.get(f.id); Chunk c = chunks.get(f.id);
...@@ -815,6 +834,7 @@ public class MVStore { ...@@ -815,6 +834,7 @@ public class MVStore {
* @return if there are any changes * @return if there are any changes
*/ */
public boolean hasUnsavedChanges() { public boolean hasUnsavedChanges() {
checkOpen();
if (mapsChanged.size() == 0) { if (mapsChanged.size() == 0) {
return false; return false;
} }
...@@ -843,6 +863,7 @@ public class MVStore { ...@@ -843,6 +863,7 @@ public class MVStore {
* @return if anything was written * @return if anything was written
*/ */
public boolean compact(int fillRate) { public boolean compact(int fillRate) {
checkOpen();
if (chunks.size() == 0) { if (chunks.size() == 0) {
// avoid division by 0 // avoid division by 0
return false; return false;
...@@ -1189,6 +1210,7 @@ public class MVStore { ...@@ -1189,6 +1210,7 @@ public class MVStore {
* @return the store version * @return the store version
*/ */
public int getStoreVersion() { public int getStoreVersion() {
checkOpen();
String x = meta.get("setting.storeVersion"); String x = meta.get("setting.storeVersion");
return x == null ? 0 : Integer.parseInt(x); return x == null ? 0 : Integer.parseInt(x);
} }
...@@ -1199,6 +1221,7 @@ public class MVStore { ...@@ -1199,6 +1221,7 @@ public class MVStore {
* @param version the new store version * @param version the new store version
*/ */
public void setStoreVersion(int version) { public void setStoreVersion(int version) {
checkOpen();
meta.put("setting.storeVersion", Integer.toString(version)); meta.put("setting.storeVersion", Integer.toString(version));
} }
...@@ -1211,6 +1234,7 @@ public class MVStore { ...@@ -1211,6 +1234,7 @@ public class MVStore {
* @param version the version to revert to * @param version the version to revert to
*/ */
public void rollbackTo(long version) { public void rollbackTo(long version) {
checkOpen();
if (!isKnownVersion(version)) { if (!isKnownVersion(version)) {
throw DataUtils.illegalArgumentException("Unknown version: " + version); throw DataUtils.illegalArgumentException("Unknown version: " + version);
} }
...@@ -1329,9 +1353,16 @@ public class MVStore { ...@@ -1329,9 +1353,16 @@ public class MVStore {
* @return the file, or null * @return the file, or null
*/ */
public FileChannel getFile() { public FileChannel getFile() {
checkOpen();
return file; return file;
} }
private void checkOpen() {
if (closed) {
throw DataUtils.illegalStateException("This store is closed");
}
}
public String toString() { public String toString() {
return DataUtils.appendMap(new StringBuilder(), config).toString(); return DataUtils.appendMap(new StringBuilder(), config).toString();
} }
......
...@@ -29,7 +29,7 @@ public class MVStoreTool { ...@@ -29,7 +29,7 @@ public class MVStoreTool {
* *
* @param args the command line arguments * @param args the command line arguments
*/ */
public static void main(String... args) throws IOException { public static void main(String... args) {
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
if ("-dump".equals(args[i])) { if ("-dump".equals(args[i])) {
String fileName = args[++i]; String fileName = args[++i];
...@@ -45,7 +45,7 @@ public class MVStoreTool { ...@@ -45,7 +45,7 @@ public class MVStoreTool {
* @param fileName the name of the file * @param fileName the name of the file
* @param writer the print writer * @param writer the print writer
*/ */
public static void dump(String fileName, Writer writer) throws IOException { public static void dump(String fileName, Writer writer) {
PrintWriter pw = new PrintWriter(writer, true); PrintWriter pw = new PrintWriter(writer, true);
if (!FileUtils.exists(fileName)) { if (!FileUtils.exists(fileName)) {
pw.println("File not found: " + fileName); pw.println("File not found: " + fileName);
...@@ -112,7 +112,7 @@ public class MVStoreTool { ...@@ -112,7 +112,7 @@ public class MVStoreTool {
} }
} catch (IOException e) { } catch (IOException e) {
pw.println("ERROR: " + e); pw.println("ERROR: " + e);
throw e; e.printStackTrace(pw);
} finally { } finally {
if (file != null) { if (file != null) {
try { try {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论