提交 2ea52348 authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore: use a separate write version for every map, so that the meta map…

MVStore: use a separate write version for every map, so that the meta map version matches the version of the other maps.
上级 bae98769
...@@ -39,6 +39,11 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -39,6 +39,11 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*/ */
protected volatile Page root; protected volatile Page root;
/**
* The version used for writing.
*/
protected long writeVersion;
private int id; private int id;
private long createVersion; private long createVersion;
private final DataType keyType; private final DataType keyType;
...@@ -70,6 +75,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -70,6 +75,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
this.id = Integer.parseInt(config.get("id")); this.id = Integer.parseInt(config.get("id"));
String x = config.get("createVersion"); String x = config.get("createVersion");
this.createVersion = x == null ? 0 : Long.parseLong(x); this.createVersion = x == null ? 0 : Long.parseLong(x);
this.writeVersion = store.getCurrentVersion();
} }
/** /**
...@@ -100,10 +106,10 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -100,10 +106,10 @@ public class MVMap<K, V> extends AbstractMap<K, V>
DataUtils.checkArgument(value != null, "The value may not be null"); DataUtils.checkArgument(value != null, "The value may not be null");
beforeWrite(); beforeWrite();
try { try {
long writeVersion = store.getCurrentVersion(); long v = writeVersion;
Page p = copyOnWrite(root, writeVersion); Page p = copyOnWrite(root, v);
p = splitRootIfNeeded(p, writeVersion); p = splitRootIfNeeded(p, v);
Object result = put(p, writeVersion, key, value); Object result = put(p, v, key, value);
newRoot(p); newRoot(p);
return (V) result; return (V) result;
} finally { } finally {
...@@ -496,7 +502,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -496,7 +502,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
beforeWrite(); beforeWrite();
try { try {
root.removeAllRecursive(); root.removeAllRecursive();
newRoot(Page.createEmpty(this, store.getCurrentVersion())); newRoot(Page.createEmpty(this, writeVersion));
} finally { } finally {
afterWrite(); afterWrite();
} }
...@@ -544,10 +550,10 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -544,10 +550,10 @@ public class MVMap<K, V> extends AbstractMap<K, V>
public V remove(Object key) { public V remove(Object key) {
beforeWrite(); beforeWrite();
try { try {
long writeVersion = store.getCurrentVersion(); long v = writeVersion;
Page p = copyOnWrite(root, writeVersion); Page p = copyOnWrite(root, v);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
V result = (V) remove(p, writeVersion, key); V result = (V) remove(p, v, key);
newRoot(p); newRoot(p);
return result; return result;
} finally { } finally {
...@@ -1014,7 +1020,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1014,7 +1020,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
// need to copy because it can change // need to copy because it can change
Page r = root; Page r = root;
if (version >= r.getVersion() && if (version >= r.getVersion() &&
(version == store.getCurrentVersion() || (version == writeVersion ||
r.getVersion() >= 0 || r.getVersion() >= 0 ||
version <= createVersion || version <= createVersion ||
store.getFile() == null)) { store.getFile() == null)) {
...@@ -1129,6 +1135,10 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1129,6 +1135,10 @@ public class MVMap<K, V> extends AbstractMap<K, V>
} }
} }
void setWriteVersion(long writeVersion) {
this.writeVersion = writeVersion;
}
@Override @Override
public String toString() { public String toString() {
return asString(null); return asString(null);
......
...@@ -46,11 +46,11 @@ public class MVMapConcurrent<K, V> extends MVMap<K, V> { ...@@ -46,11 +46,11 @@ public class MVMapConcurrent<K, V> extends MVMap<K, V> {
// even if the result is the same, we still update the value // even if the result is the same, we still update the value
// (otherwise compact doesn't work) // (otherwise compact doesn't work)
get(key); get(key);
long writeVersion = store.getCurrentVersion(); long v = writeVersion;
synchronized (this) { synchronized (this) {
Page p = copyOnWrite(root, writeVersion); Page p = copyOnWrite(root, v);
p = splitRootIfNeeded(p, writeVersion); p = splitRootIfNeeded(p, v);
V result = (V) put(p, writeVersion, key, value); V result = (V) put(p, v, key, value);
newRoot(p); newRoot(p);
return result; return result;
} }
...@@ -73,10 +73,10 @@ public class MVMapConcurrent<K, V> extends MVMap<K, V> { ...@@ -73,10 +73,10 @@ public class MVMapConcurrent<K, V> extends MVMap<K, V> {
if (result == null) { if (result == null) {
return null; return null;
} }
long writeVersion = store.getCurrentVersion(); long v = writeVersion;
synchronized (this) { synchronized (this) {
Page p = copyOnWrite(root, writeVersion); Page p = copyOnWrite(root, v);
result = (V) remove(p, writeVersion, key); result = (V) remove(p, v, key);
newRoot(p); newRoot(p);
} }
return result; return result;
......
...@@ -49,7 +49,7 @@ TODO: ...@@ -49,7 +49,7 @@ TODO:
TestMVStoreDataLoss TestMVStoreDataLoss
TransactionStore: TransactionStore:
- support reading the undo log - write to the undo log _before_ a change (WAL style)
MVStore: MVStore:
- rolling docs review: at "Features" - rolling docs review: at "Features"
...@@ -109,6 +109,8 @@ MVStore: ...@@ -109,6 +109,8 @@ MVStore:
- more consistent null handling (keys/values sometimes may be null) - more consistent null handling (keys/values sometimes may be null)
- autocommit (to avoid having to call commit, - autocommit (to avoid having to call commit,
as it could be called too often or it is easily forgotten) as it could be called too often or it is easily forgotten)
- remove features that are not really needed; simplify the code
- rename "store" to "save", as store collides with storeVersion
*/ */
...@@ -405,9 +407,9 @@ public class MVStore { ...@@ -405,9 +407,9 @@ public class MVStore {
private Chunk getChunkForVersion(long version) { private Chunk getChunkForVersion(long version) {
for (int chunkId = lastChunkId;; chunkId--) { for (int chunkId = lastChunkId;; chunkId--) {
Chunk x = chunks.get(chunkId); Chunk x = chunks.get(chunkId);
if (x == null || x.version < version) { if (x == null) {
return null; return null;
} else if (x.version == version) { } else if (x.version <= version) {
return x; return x;
} }
} }
...@@ -619,7 +621,9 @@ public class MVStore { ...@@ -619,7 +621,9 @@ public class MVStore {
private void readFileHeader() { private void readFileHeader() {
// we don't have a valid header yet // we don't have a valid header yet
currentVersion = -1; currentVersion = -1;
// read the last block of the file, and then two first blocks // we don't know which chunk is the newest
long newestChunk = -1;
// read the last block of the file, and then the two first blocks
ByteBuffer buff = ByteBuffer.allocate(3 * BLOCK_SIZE); ByteBuffer buff = ByteBuffer.allocate(3 * BLOCK_SIZE);
buff.limit(BLOCK_SIZE); buff.limit(BLOCK_SIZE);
fileReadCount++; fileReadCount++;
...@@ -653,25 +657,28 @@ public class MVStore { ...@@ -653,25 +657,28 @@ public class MVStore {
if (check != checksum) { if (check != checksum) {
continue; continue;
} }
long version = Long.parseLong(m.get("version")); long chunk = Long.parseLong(m.get("chunk"));
if (version > currentVersion) { if (chunk > newestChunk) {
newestChunk = chunk;
fileHeader = m; fileHeader = m;
rootChunkStart = Long.parseLong(m.get("rootChunk")); rootChunkStart = Long.parseLong(m.get("rootChunk"));
creationTime = Long.parseLong(m.get("creationTime")); creationTime = Long.parseLong(m.get("creationTime"));
currentVersion = version;
lastMapId = Integer.parseInt(m.get("lastMapId")); lastMapId = Integer.parseInt(m.get("lastMapId"));
currentVersion = Long.parseLong(m.get("version"));
} }
} }
if (currentVersion < 0) { if (currentVersion < 0) {
throw DataUtils.newIllegalStateException( throw DataUtils.newIllegalStateException(
DataUtils.ERROR_FILE_CORRUPT, "File header is corrupt: {0}", fileName); DataUtils.ERROR_FILE_CORRUPT, "File header is corrupt: {0}", fileName);
} }
setWriteVersion(currentVersion);
lastStoredVersion = -1; lastStoredVersion = -1;
} }
private byte[] getFileHeaderBytes() { private byte[] getFileHeaderBytes() {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
fileHeader.put("lastMapId", "" + lastMapId); fileHeader.put("lastMapId", "" + lastMapId);
fileHeader.put("chunk", "" + lastChunkId);
fileHeader.put("rootChunk", "" + rootChunkStart); fileHeader.put("rootChunk", "" + rootChunkStart);
fileHeader.put("version", "" + currentVersion); fileHeader.put("version", "" + currentVersion);
DataUtils.appendMap(buff, fileHeader); DataUtils.appendMap(buff, fileHeader);
...@@ -709,8 +716,10 @@ public class MVStore { ...@@ -709,8 +716,10 @@ public class MVStore {
return; return;
} }
if (!readOnly) { if (!readOnly) {
if (hasUnsavedChanges()) { stopBackgroundThread();
if (hasUnsavedChanges() || lastCommittedVersion != currentVersion) {
rollbackTo(lastCommittedVersion); rollbackTo(lastCommittedVersion);
metaChanged = true;
store(false); store(false);
} }
} }
...@@ -718,11 +727,17 @@ public class MVStore { ...@@ -718,11 +727,17 @@ public class MVStore {
} }
/** /**
* Close the file and the store, without writing anything. * Close the file and the store, without writing anything. This will stop
* This will stop the background thread. * the background thread. This method ignores all errors.
*/ */
public void closeImmediately() { public void closeImmediately() {
try {
closeFile(false); closeFile(false);
} catch (Exception e) {
if (backgroundExceptionListener != null) {
backgroundExceptionListener.exceptionThrown(e);
}
}
} }
private void closeFile(boolean shrinkIfPossible) { private void closeFile(boolean shrinkIfPossible) {
...@@ -782,7 +797,16 @@ public class MVStore { ...@@ -782,7 +797,16 @@ public class MVStore {
* @return the new version * @return the new version
*/ */
public long incrementVersion() { public long incrementVersion() {
return ++currentVersion; long v = ++currentVersion;
setWriteVersion(v);
return v;
}
private void setWriteVersion(long version) {
for (MVMap<?, ?> map : maps.values()) {
map.setWriteVersion(version);
}
meta.setWriteVersion(version);
} }
/** /**
...@@ -796,7 +820,7 @@ public class MVStore { ...@@ -796,7 +820,7 @@ public class MVStore {
* @return the new version * @return the new version
*/ */
public long commit() { public long commit() {
long v = ++currentVersion; long v = incrementVersion();
lastCommittedVersion = v; lastCommittedVersion = v;
if (writeDelay == 0) { if (writeDelay == 0) {
store(false); store(false);
...@@ -842,12 +866,12 @@ public class MVStore { ...@@ -842,12 +866,12 @@ public class MVStore {
DataUtils.ERROR_WRITING_FAILED, "This store is read-only"); DataUtils.ERROR_WRITING_FAILED, "This store is read-only");
} }
int currentUnsavedPageCount = unsavedPageCount; int currentUnsavedPageCount = unsavedPageCount;
long storeVersion = currentStoreVersion = currentVersion; currentStoreVersion = currentVersion;
long version = incrementVersion();
if (file == null) { if (file == null) {
return version; return incrementVersion();
} }
long storeVersion = currentStoreVersion;
long version = ++currentVersion;
long time = getTime(); long time = getTime();
lastStoreTime = time; lastStoreTime = time;
if (temp) { if (temp) {
...@@ -867,6 +891,7 @@ public class MVStore { ...@@ -867,6 +891,7 @@ public class MVStore {
meta.remove("rollbackOnOpen"); meta.remove("rollbackOnOpen");
retainChunk = null; retainChunk = null;
} }
// the last chunk was not completely correct in the last store() // the last chunk was not completely correct in the last store()
// this needs to be updated now (it's better not to update right after // this needs to be updated now (it's better not to update right after
// storing, because that would modify the meta map again) // storing, because that would modify the meta map again)
...@@ -890,6 +915,7 @@ public class MVStore { ...@@ -890,6 +915,7 @@ public class MVStore {
ArrayList<MVMap<?, ?>> changed = New.arrayList(); ArrayList<MVMap<?, ?>> changed = New.arrayList();
for (MVMap<?, ?> m : list) { for (MVMap<?, ?> m : list) {
if (m != meta) { if (m != meta) {
m.setWriteVersion(version);
long v = m.getVersion(); long v = m.getVersion();
if (v >= 0 && m.getVersion() >= lastStoredVersion) { if (v >= 0 && m.getVersion() >= lastStoredVersion) {
MVMap<?, ?> r = m.openVersion(storeVersion); MVMap<?, ?> r = m.openVersion(storeVersion);
...@@ -930,8 +956,9 @@ public class MVStore { ...@@ -930,8 +956,9 @@ public class MVStore {
} }
meta.put("chunk." + c.id, c.asString()); meta.put("chunk." + c.id, c.asString());
meta.setWriteVersion(version);
// this will modify maxLengthLive, but // this will (again) modify maxLengthLive, but
// the correct value is written in the chunk header // the correct value is written in the chunk header
buff = meta.getRoot().writeUnsavedRecursive(c, buff); buff = meta.getRoot().writeUnsavedRecursive(c, buff);
...@@ -1001,8 +1028,10 @@ public class MVStore { ...@@ -1001,8 +1028,10 @@ public class MVStore {
// some pages might have been changed in the meantime (in the newest version) // some pages might have been changed in the meantime (in the newest version)
unsavedPageCount = Math.max(0, unsavedPageCount - currentUnsavedPageCount); unsavedPageCount = Math.max(0, unsavedPageCount - currentUnsavedPageCount);
currentStoreVersion = -1; currentStoreVersion = -1;
if (!temp) {
metaChanged = false; metaChanged = false;
lastStoredVersion = storeVersion; lastStoredVersion = storeVersion;
}
return version; return version;
} }
...@@ -1138,7 +1167,7 @@ public class MVStore { ...@@ -1138,7 +1167,7 @@ public class MVStore {
for (MVMap<?, ?> m : maps.values()) { for (MVMap<?, ?> m : maps.values()) {
if (!m.isClosed()) { if (!m.isClosed()) {
long v = m.getVersion(); long v = m.getVersion();
if (v >= 0 && v >= lastStoredVersion) { if (v >= 0 && v > lastStoredVersion) {
return true; return true;
} }
} }
...@@ -1564,6 +1593,8 @@ public class MVStore { ...@@ -1564,6 +1593,8 @@ public class MVStore {
freedPages.clear(); freedPages.clear();
} }
currentVersion = version; currentVersion = version;
setWriteVersion(version);
lastCommittedVersion = version;
metaChanged = false; metaChanged = false;
return; return;
} }
...@@ -1584,17 +1615,33 @@ public class MVStore { ...@@ -1584,17 +1615,33 @@ public class MVStore {
meta.rollbackTo(version); meta.rollbackTo(version);
metaChanged = false; metaChanged = false;
boolean loadFromFile = false; boolean loadFromFile = false;
Chunk last = chunks.get(lastChunkId); // get the largest chunk with a version
if (last != null) { // higher or equal the requested version
if (last.version >= version) { int removeChunksNewerThan = -1;
for (int chunkId = lastChunkId;; chunkId--) {
Chunk x = chunks.get(chunkId);
if (x == null) {
break;
} else if (x.version >= version) {
removeChunksNewerThan = x.id;
}
}
if (removeChunksNewerThan >= 0 && lastChunkId > removeChunksNewerThan) {
revertTemp(version); revertTemp(version);
loadFromFile = true; loadFromFile = true;
do { Chunk last = null;
last = chunks.remove(lastChunkId); while (true) {
last = chunks.get(lastChunkId);
if (last == null) {
break;
} else if (last.id <= removeChunksNewerThan) {
break;
}
chunks.remove(lastChunkId);
int len = MathUtils.roundUpInt(last.length, BLOCK_SIZE) + BLOCK_SIZE; int len = MathUtils.roundUpInt(last.length, BLOCK_SIZE) + BLOCK_SIZE;
freeSpace.free(last.start, len); freeSpace.free(last.start, len);
lastChunkId--; lastChunkId--;
} while (last.version > version && chunks.size() > 0); }
rootChunkStart = last.start; rootChunkStart = last.start;
writeFileHeader(); writeFileHeader();
// need to write the header at the end of the file as well, // need to write the header at the end of the file as well,
...@@ -1609,7 +1656,6 @@ public class MVStore { ...@@ -1609,7 +1656,6 @@ public class MVStore {
readFileHeader(); readFileHeader();
readMeta(); readMeta();
} }
}
for (MVMap<?, ?> m : New.arrayList(maps.values())) { for (MVMap<?, ?> m : New.arrayList(maps.values())) {
int id = m.getId(); int id = m.getId();
if (m.getCreateVersion() >= version) { if (m.getCreateVersion() >= version) {
...@@ -1622,8 +1668,11 @@ public class MVStore { ...@@ -1622,8 +1668,11 @@ public class MVStore {
m.setRootPos(root, -1); m.setRootPos(root, -1);
} }
} }
} }
this.currentVersion = version; currentVersion = version;
setWriteVersion(version);
lastCommittedVersion = version;
} }
private void revertTemp(long storeVersion) { private void revertTemp(long storeVersion) {
...@@ -1727,16 +1776,15 @@ public class MVStore { ...@@ -1727,16 +1776,15 @@ public class MVStore {
checkOpen(); checkOpen();
DataUtils.checkArgument(map != meta, DataUtils.checkArgument(map != meta,
"Renaming the meta map is not allowed"); "Renaming the meta map is not allowed");
if (map.getName().equals(newName)) { int id = map.getId();
String oldName = getMapName(id);
if (oldName.equals(newName)) {
return; return;
} }
DataUtils.checkArgument( DataUtils.checkArgument(
!meta.containsKey("name." + newName), !meta.containsKey("name." + newName),
"A map named {0} already exists", newName); "A map named {0} already exists", newName);
int id = map.getId();
String oldName = getMapName(id);
markMetaChanged(); markMetaChanged();
meta.remove("map." + id);
meta.remove("name." + oldName); meta.remove("name." + oldName);
meta.put("map." + id, map.asString(newName)); meta.put("map." + id, map.asString(newName));
meta.put("name." + newName, Integer.toString(id)); meta.put("name." + newName, Integer.toString(id));
...@@ -1762,13 +1810,16 @@ public class MVStore { ...@@ -1762,13 +1810,16 @@ public class MVStore {
} }
// could also store when there are many unsaved pages, // could also store when there are many unsaved pages,
// but according to a test it doesn't really help // but according to a test it doesn't really help
if (lastCommittedVersion >= currentVersion) { if (lastStoredVersion >= lastCommittedVersion) {
return; return;
} }
long time = getTime(); long time = getTime();
if (time <= lastStoreTime + writeDelay) { if (time <= lastStoreTime + writeDelay) {
return; return;
} }
if (!hasUnsavedChanges()) {
return;
}
try { try {
store(true); store(true);
} catch (Exception e) { } catch (Exception e) {
......
...@@ -913,6 +913,11 @@ public class TransactionStore { ...@@ -913,6 +913,11 @@ public class TransactionStore {
newValue.value = value; newValue.value = value;
if (current == null) { if (current == null) {
// a new value // a new value
int todo;
// either write the log before the data (and handle this case in rollback)
// or ensure/document concurrent commits are not allowed
VersionedValue old = map.putIfAbsent(key, newValue); VersionedValue old = map.putIfAbsent(key, newValue);
if (old == null) { if (old == null) {
transaction.log(opType, mapId, key, current); transaction.log(opType, mapId, key, current);
......
...@@ -223,30 +223,30 @@ public class MVRTreeMap<V> extends MVMap<SpatialKey, V> { ...@@ -223,30 +223,30 @@ public class MVRTreeMap<V> extends MVMap<SpatialKey, V> {
private Object putOrAdd(SpatialKey key, V value, boolean alwaysAdd) { private Object putOrAdd(SpatialKey key, V value, boolean alwaysAdd) {
beforeWrite(); beforeWrite();
try { try {
long writeVersion = store.getCurrentVersion(); long v = writeVersion;
Page p = copyOnWrite(root, writeVersion); Page p = copyOnWrite(root, v);
Object result; Object result;
if (alwaysAdd || get(key) == null) { if (alwaysAdd || get(key) == null) {
if (p.getMemory() > store.getPageSize() && p.getKeyCount() > 1) { if (p.getMemory() > store.getPageSize() && p.getKeyCount() > 1) {
// only possible if this is the root, else we would have split earlier // only possible if this is the root, else we would have split earlier
// (this requires maxPageSize is fixed) // (this requires maxPageSize is fixed)
long totalCount = p.getTotalCount(); long totalCount = p.getTotalCount();
Page split = split(p, writeVersion); Page split = split(p, v);
Object k1 = getBounds(p); Object k1 = getBounds(p);
Object k2 = getBounds(split); Object k2 = getBounds(split);
Object[] keys = { k1, k2 }; Object[] keys = { k1, k2 };
long[] children = { p.getPos(), split.getPos(), 0 }; long[] children = { p.getPos(), split.getPos(), 0 };
Page[] childrenPages = { p, split, null }; Page[] childrenPages = { p, split, null };
long[] counts = { p.getTotalCount(), split.getTotalCount(), 0 }; long[] counts = { p.getTotalCount(), split.getTotalCount(), 0 };
p = Page.create(this, writeVersion, 2, p = Page.create(this, v, 2,
keys, null, children, childrenPages, counts, keys, null, children, childrenPages, counts,
totalCount, 0, 0); totalCount, 0, 0);
// now p is a node; continues // now p is a node; continues
} }
add(p, writeVersion, key, value); add(p, v, key, value);
result = null; result = null;
} else { } else {
result = set(p, writeVersion, key, value); result = set(p, v, key, value);
} }
newRoot(p); newRoot(p);
return result; return result;
......
...@@ -123,7 +123,7 @@ public class TestMVStore extends TestBase { ...@@ -123,7 +123,7 @@ public class TestMVStore extends TestBase {
assertTrue(e != null); assertTrue(e != null);
assertEquals(DataUtils.ERROR_WRITING_FAILED, DataUtils.getErrorCode(e.getMessage())); assertEquals(DataUtils.ERROR_WRITING_FAILED, DataUtils.getErrorCode(e.getMessage()));
s.close(); s.closeImmediately();
FileUtils.delete(fileName); FileUtils.delete(fileName);
} }
...@@ -237,7 +237,14 @@ public class TestMVStore extends TestBase { ...@@ -237,7 +237,14 @@ public class TestMVStore extends TestBase {
m.put(2, "World"); m.put(2, "World");
Thread.sleep(5); Thread.sleep(5);
// must not store, as nothing has been committed yet // must not store, as nothing has been committed yet
assertEquals(v, s.getCurrentVersion()); s.closeImmediately();
s = new MVStore.Builder().
writeDelay(1).
fileName(fileName).
open();
m = s.openMap("data");
assertEquals(null, m.get(2));
m.put(2, "World");
s.commit(); s.commit();
m.put(3, "!"); m.put(3, "!");
...@@ -251,19 +258,6 @@ public class TestMVStore extends TestBase { ...@@ -251,19 +258,6 @@ public class TestMVStore extends TestBase {
Thread.sleep(1); Thread.sleep(1);
} }
s.close(); s.close();
s = new MVStore.Builder().
fileName(fileName).
open();
m = s.openMap("data");
assertEquals("Hello", m.get(1));
assertEquals("World", m.get(2));
assertFalse(m.containsKey(3));
String data = new String(new char[1000]).replace((char) 0, 'x');
for (int i = 0; i < 1000; i++) {
m.put(i, data);
}
s.close();
s = new MVStore.Builder(). s = new MVStore.Builder().
fileName(fileName). fileName(fileName).
...@@ -472,13 +466,21 @@ public class TestMVStore extends TestBase { ...@@ -472,13 +466,21 @@ public class TestMVStore extends TestBase {
s.close(); s.close();
} }
private void testFileHeaderCorruption() throws IOException { private void testFileHeaderCorruption() throws Exception {
String fileName = getBaseDir() + "/testFileHeader.h3"; String fileName = getBaseDir() + "/testFileHeader.h3";
MVStore s = openStore(fileName); MVStore s = openStore(fileName);
s.setRetentionTime(10);
MVMap<Integer, Integer> map = s.openMap("test"); MVMap<Integer, Integer> map = s.openMap("test");
for (int i = 0; i < 5; i++) {
s.setStoreVersion(i);
s.store();
}
// ensure the oldest chunks can be overwritten
Thread.sleep(11);
s.compact(50);
map.put(10, 100); map.put(10, 100);
FilePath f = FilePath.get(s.getFileName());
s.store(); s.store();
FilePath f = FilePath.get(s.getFileName());
s.close(); s.close();
int blockSize = 4 * 1024; int blockSize = 4 * 1024;
// test corrupt file headers // test corrupt file headers
...@@ -652,13 +654,13 @@ public class TestMVStore extends TestBase { ...@@ -652,13 +654,13 @@ public class TestMVStore extends TestBase {
s.setStoreVersion(1); s.setStoreVersion(1);
s.close(); s.close();
s = MVStore.open(fileName); s = MVStore.open(fileName);
assertEquals(0, s.getCurrentVersion()); assertEquals(1, s.getCurrentVersion());
assertEquals(0, s.getStoreVersion()); assertEquals(0, s.getStoreVersion());
s.setStoreVersion(1); s.setStoreVersion(1);
s.store(); s.store();
s.close(); s.close();
s = MVStore.open(fileName); s = MVStore.open(fileName);
assertEquals(1, s.getCurrentVersion()); assertEquals(2, s.getCurrentVersion());
assertEquals(1, s.getStoreVersion()); assertEquals(1, s.getStoreVersion());
s.close(); s.close();
} }
...@@ -698,6 +700,7 @@ public class TestMVStore extends TestBase { ...@@ -698,6 +700,7 @@ public class TestMVStore extends TestBase {
map.put(new Object[1], new Object[]{1, "2"}); map.put(new Object[1], new Object[]{1, "2"});
s.store(); s.store();
s.close(); s.close();
s = new MVStore.Builder().fileName(fileName).open(); s = new MVStore.Builder().fileName(fileName).open();
map = s.openMap("test"); map = s.openMap("test");
assertEquals("Hello", map.get(1).toString()); assertEquals("Hello", map.get(1).toString());
...@@ -938,6 +941,8 @@ public class TestMVStore extends TestBase { ...@@ -938,6 +941,8 @@ public class TestMVStore extends TestBase {
s.rollbackTo(1); s.rollbackTo(1);
assertEquals(1, s.getCurrentVersion()); assertEquals(1, s.getCurrentVersion());
assertEquals("Hello", m.get("1")); assertEquals("Hello", m.get("1"));
// so a new version is created
m.put("1", "Hello");
long v2 = s.store(); long v2 = s.store();
assertEquals(2, v2); assertEquals(2, v2);
......
...@@ -441,7 +441,7 @@ public class TestTransactionStore extends TestBase { ...@@ -441,7 +441,7 @@ public class TestTransactionStore extends TestBase {
return; return;
} }
statements.get(0).execute( statements.get(0).execute(
"drop table if exists test"); "drop table if exists test cascade");
statements.get(0).execute( statements.get(0).execute(
"create table test(id int primary key, name varchar(255))"); "create table test(id int primary key, name varchar(255))");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论