提交 900b5667 authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore: simpler API

上级 e845504a
......@@ -23,6 +23,7 @@ Change Log
</li><li>Issue 73: MySQL compatibility: support REPLACE, patch by Cemo Koc.
</li><li>The spatial index now works in MVCC mode when using the MVStore storage.
</li><li>MVStore: concurrency problems have been fixed.
The API has been simplified.
</li><li>Improve error message when dropping an index that belongs to a constraint,
specify constraint in error message.
</li><li>Issue 518: java.sql.Connection.commit() freezes after LOB modification with EXCLUSIVE connection
......
......@@ -59,7 +59,7 @@ TransactionStore:
MVStore:
- automated 'kill process' and 'power failure' test
- update checkstyle
- auto-compact from time to time and on close
- feature to auto-compact from time to time and on close
- test and possibly improve compact operation (for large dbs)
- possibly split chunk metadata into immutable and mutable
- compact: avoid processing pages using a counting bloom filter
......@@ -115,6 +115,19 @@ MVStore:
- support log structured merge style operations (blind writes)
using one map per level plus bloom filter
- have a strict call order MVStore -> MVMap -> Page -> FileStore
- autocommit mode (default) and manual mode
- manual mode: combine commit and store;
rollback only to chunk
- rename writeDelay to commitDelay, default 1 s
- rollback() to rollback to the latest commit; throws exception
in autocommit mode
- fix documentation (including examples)
- autocommit commits, stores, and compacts from time to time;
the background thread should wait at least 90% of the
configured write delay to store changes
- currently, uncommitted changes are stored if there are many transient changes,
and rolled back when opening - is this really needed?
- compact* should also store uncommitted changes (if there are any)
*/
......@@ -141,6 +154,7 @@ public class MVStore {
* The background thread, if any.
*/
volatile Thread backgroundThread;
final Object backgroundThreadSync = new Object();
private volatile boolean reuseSpace = true;
......@@ -176,9 +190,9 @@ public class MVStore {
new ConcurrentHashMap<Long, HashMap<Integer, Chunk>>();
/**
* The metadata map.
* The metadata map. Write access to this map needs to be synchronized on the store.
*/
private MVMapConcurrent<String, String> meta;
private MVMap<String, String> meta;
private final ConcurrentHashMap<Integer, MVMap<?, ?>> maps =
new ConcurrentHashMap<Integer, MVMap<?, ?>>();
......@@ -208,6 +222,12 @@ public class MVStore {
*/
private long lastStoredVersion;
/**
* The estimated number of unsaved pages
* (this number may not be completely accurate,
* because it may be changed concurrently, and
* because temporary pages are counted)
*/
private int unsavedPageCount;
private int unsavedPageCountMax;
private boolean storeNeeded;
......@@ -220,11 +240,6 @@ public class MVStore {
private long lastStoreTime;
/**
* To which version to roll back when opening the store after a crash.
*/
private long lastCommittedVersion;
/**
* The earliest chunk to retain, if any.
*/
......@@ -235,6 +250,8 @@ public class MVStore {
*/
private volatile long currentStoreVersion = -1;
private Thread currentStoreThread;
private volatile boolean metaChanged;
/**
......@@ -256,7 +273,7 @@ public class MVStore {
pageSplitSize = o == null ? 6 * 1024 : (Integer) o;
o = config.get("backgroundExceptionHandler");
this.backgroundExceptionHandler = (UncaughtExceptionHandler) o;
meta = new MVMapConcurrent<String, String>(StringDataType.INSTANCE, StringDataType.INSTANCE);
meta = new MVMap<String, String>(StringDataType.INSTANCE, StringDataType.INSTANCE);
HashMap<String, String> c = New.hashMap();
c.put("id", "0");
c.put("createVersion", Long.toString(currentVersion));
......@@ -320,10 +337,6 @@ public class MVStore {
readMeta();
}
}
long rollback = DataUtils.parseLong(meta.get("rollbackOnOpen"), -1);
if (rollback != -1) {
rollbackTo(rollback);
}
} catch (IllegalStateException e) {
try {
closeStore(false);
......@@ -337,7 +350,6 @@ public class MVStore {
}
}
lastStoreTime = getTime();
this.lastCommittedVersion = currentVersion;
// setWriteDelay starts the thread, but only if
// the parameter is different than the current value
......@@ -403,7 +415,7 @@ public class MVStore {
* @param builder the map builder
* @return the map
*/
public <M extends MVMap<K, V>, K, V> M openMap(String name, MVMap.MapBuilder<M, K, V> builder) {
public synchronized <M extends MVMap<K, V>, K, V> M openMap(String name, MVMap.MapBuilder<M, K, V> builder) {
checkOpen();
String x = meta.get("name." + name);
int id;
......@@ -445,17 +457,17 @@ public class MVStore {
* Get the metadata map. This data is for informational purposes only. The
* data is subject to change in future versions.
* <p>
* The data should not be modified (doing so may corrupt the store). Writing
* to it is not always detected as a modification, so that changes to it
* might not be stored.
* The data in this map should not be modified (changing system data may
* corrupt the store). If modifications are needed, they need be
* synchronized on the store.
* <p>
* It contains the following entries:
*
* The metadata map contains the following entries:
* <pre>
* chunk.{chunkId} = {chunk metadata}
* name.{name} = {mapId}
* map.{mapId} = {map metadata}
* root.{mapId} = {root position}
* chunk.{chunkId} = {chunk metadata}
* setting.storeVersion = {version}
* </pre>
*
* @return the metadata map
......@@ -636,9 +648,8 @@ public class MVStore {
}
/**
* Close the file and the store. If there are any committed but unsaved
* changes, they are written to disk first. If any temporary data was
* written but not committed, this is rolled back. All open maps are closed.
* Close the file and the store. If there are any uncommitted changes, they
* are written to disk first. All open maps are closed.
* <p>
* It is not allowed to concurrently call close and store.
*/
......@@ -648,18 +659,8 @@ public class MVStore {
}
if (fileStore != null && !fileStore.isReadOnly()) {
stopBackgroundThread();
if (currentStoreVersion >= 0) {
// in this case, store is called manually in another thread
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_WRITING_FAILED,
"Can not close while storing");
}
if (hasUnsavedChanges() || lastCommittedVersion != currentVersion) {
rollbackTo(lastCommittedVersion);
metaChanged = true;
store(false);
if (hasUnsavedChanges()) {
store();
}
}
closeStore(true);
......@@ -739,17 +740,6 @@ public class MVStore {
return c;
}
/**
* Increment the current version, without committing the changes.
*
* @return the new version
*/
public long incrementVersion() {
long v = ++currentVersion;
setWriteVersion(v);
return v;
}
private void setWriteVersion(long version) {
for (MVMap<?, ?> map : maps.values()) {
map.setWriteVersion(version);
......@@ -768,11 +758,11 @@ public class MVStore {
* @return the new version
*/
public long commit() {
long v = incrementVersion();
lastCommittedVersion = v;
if (writeDelay == 0) {
store(false);
if (fileStore != null) {
return store();
}
long v = ++currentVersion;
setWriteVersion(v);
return v;
}
......@@ -781,29 +771,17 @@ public class MVStore {
* there are no unsaved changes, otherwise it increments the current version
* and stores the data (for file based stores).
* <p>
* One store operation may run at any time.
* At most one store operation may run at any time.
*
* @return the new version (incremented if there were changes)
*/
public long store() {
checkOpen();
return store(false);
}
/**
* Store changes. Changes that are marked as temporary are rolled back after
* a restart.
*
* @param temp whether the changes are only temporary (not committed), and
* should be rolled back after a crash
* @return the new version (incremented if there were changes)
*/
private synchronized long store(boolean temp) {
public synchronized long store() {
if (closed) {
return currentVersion;
}
if (fileStore == null) {
return incrementVersion();
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_WRITING_FAILED, "This is an in-memory store");
}
if (currentStoreVersion >= 0) {
// store is possibly called within store, if the meta map changed
......@@ -818,37 +796,24 @@ public class MVStore {
}
try {
currentStoreVersion = currentVersion;
return storeNow(temp);
currentStoreThread = Thread.currentThread();
return storeNow();
} finally {
// in any case reset the current store version,
// to allow closing the store
currentStoreVersion = -1;
currentStoreThread = null;
}
}
private long storeNow(boolean temp) {
private long storeNow() {
int currentUnsavedPageCount = unsavedPageCount;
long storeVersion = currentStoreVersion;
long version = ++currentVersion;
setWriteVersion(version);
long time = getTime();
lastStoreTime = time;
if (temp) {
meta.put("rollbackOnOpen", Long.toString(lastCommittedVersion));
// find the oldest chunk to retain
long minVersion = Long.MAX_VALUE;
Chunk minChunk = null;
for (Chunk c : chunks.values()) {
if (c.version < minVersion) {
minVersion = c.version;
minChunk = c;
}
}
retainChunk = minChunk;
} else {
lastCommittedVersion = version;
meta.remove("rollbackOnOpen");
retainChunk = null;
}
// 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
......@@ -983,10 +948,9 @@ public class MVStore {
// some pages might have been changed in the meantime (in the newest version)
unsavedPageCount = Math.max(0, unsavedPageCount - currentUnsavedPageCount);
if (!temp) {
metaChanged = false;
lastStoredVersion = storeVersion;
}
return version;
}
......@@ -1053,7 +1017,7 @@ public class MVStore {
if (v > storeVersion) {
continue;
}
Map<Integer, Chunk> freed = e.getValue();
HashMap<Integer, Chunk> freed = e.getValue();
for (Chunk f : freed.values()) {
Chunk c = chunks.get(f.id);
if (c == null) {
......@@ -1146,6 +1110,9 @@ public class MVStore {
* @return if there are any changes
*/
public boolean hasUnsavedChanges() {
;
// TODO maybe private; rename to hasUncommittedChanges
checkOpen();
if (metaChanged) {
return true;
......@@ -1281,7 +1248,7 @@ public class MVStore {
}
/**
* Force all changes to be written to the storage. The default
* Force all stored changes to be written to the storage. The default
* implementation calls FileChannel.force(true).
*/
public void sync() {
......@@ -1484,12 +1451,14 @@ public class MVStore {
Chunk c = getChunk(pos);
long version = currentVersion;
if (map == meta && currentStoreVersion >= 0) {
if (Thread.currentThread() == currentStoreThread) {
// if the meta map is modified while storing,
// then this freed page needs to be registered
// with the stored chunk, so that the old chunk
// can be re-used
version = currentStoreVersion;
}
}
registerFreePage(version, c.id, DataUtils.getPageMaxLength(pos), 1);
}
......@@ -1663,9 +1632,8 @@ public class MVStore {
}
/**
* Get the maximum number of unsaved pages. If this number is exceeded,
* the unsaved changes are stored to disk, including uncommitted changes.
* Saved uncommitted changes are rolled back when opening the store.
* Get the maximum number of unsaved pages. If this number is exceeded, the
* unsaved changes are stored to disk.
*
* @return the number of maximum unsaved pages
*/
......@@ -1677,8 +1645,8 @@ public class MVStore {
* Increment the number of unsaved pages.
*/
void registerUnsavedPage() {
unsavedPageCount++;
if (unsavedPageCount > unsavedPageCountMax && unsavedPageCountMax > 0) {
int count = ++unsavedPageCount;
if (count > unsavedPageCountMax && unsavedPageCountMax > 0) {
storeNeeded = true;
}
}
......@@ -1689,7 +1657,7 @@ public class MVStore {
void beforeWrite() {
if (storeNeeded) {
storeNeeded = false;
store(true);
store();
}
}
......@@ -1711,12 +1679,22 @@ public class MVStore {
*
* @param version the new store version
*/
public void setStoreVersion(int version) {
public synchronized void setStoreVersion(int version) {
checkOpen();
markMetaChanged();
meta.put("setting.storeVersion", Integer.toString(version));
}
/**
* Revert to the beginning of the current version.
*/
public void rollback() {
;
// TODO document and test
rollbackTo(currentVersion);
}
/**
* Revert to the beginning of the given version. All later changes (stored
* or not) are forgotten. All maps that were created later are closed. A
......@@ -1741,7 +1719,6 @@ public class MVStore {
freedPageSpace.clear();
currentVersion = version;
setWriteVersion(version);
lastCommittedVersion = version;
metaChanged = false;
return;
}
......@@ -1816,12 +1793,10 @@ public class MVStore {
// rollback might have rolled back the stored chunk metadata as well
Chunk c = chunks.get(lastChunkId - 1);
if (c != null) {
markMetaChanged();
meta.put("chunk." + c.id, c.asString());
}
currentVersion = version;
setWriteVersion(version);
lastCommittedVersion = version;
}
private void revertTemp(long storeVersion) {
......@@ -1847,15 +1822,6 @@ public class MVStore {
return currentVersion;
}
/**
* Get the last committed version.
*
* @return the version
*/
public long getCommittedVersion() {
return lastCommittedVersion;
}
/**
* Get the file store.
*
......@@ -1889,7 +1855,7 @@ public class MVStore {
* @param map the map
* @param newName the new name
*/
public void renameMap(MVMap<?, ?> map, String newName) {
public synchronized void renameMap(MVMap<?, ?> map, String newName) {
checkOpen();
DataUtils.checkArgument(map != meta,
"Renaming the meta map is not allowed");
......@@ -1912,7 +1878,7 @@ public class MVStore {
*
* @param map the map
*/
public void removeMap(MVMap<?, ?> map) {
public synchronized void removeMap(MVMap<?, ?> map) {
checkOpen();
DataUtils.checkArgument(map != meta,
"Removing the meta map is not allowed");
......@@ -1932,7 +1898,7 @@ public class MVStore {
* @param id the map id
* @return the name
*/
String getMapName(int id) {
synchronized String getMapName(int id) {
String m = meta.get("map." + id);
return DataUtils.parseMap(m).get("name");
}
......@@ -1941,14 +1907,13 @@ public class MVStore {
* Store all unsaved changes, if there are any that are committed.
*/
void storeInBackground() {
if (closed || unsavedPageCount == 0) {
if (unsavedPageCount == 0 || closed) {
return;
}
// could also store when there are many unsaved pages,
// but according to a test it doesn't really help
if (lastStoredVersion >= lastCommittedVersion) {
return;
}
long time = getTime();
if (time <= lastStoreTime + writeDelay) {
return;
......@@ -1957,7 +1922,7 @@ public class MVStore {
return;
}
try {
store(true);
store();
} catch (Exception e) {
if (backgroundExceptionHandler != null) {
backgroundExceptionHandler.uncaughtException(null, e);
......@@ -1981,13 +1946,13 @@ public class MVStore {
}
private void stopBackgroundThread() {
if (backgroundThread == null) {
Thread t = backgroundThread;
if (t == null) {
return;
}
Thread t = backgroundThread;
backgroundThread = null;
synchronized (this) {
notify();
synchronized (backgroundThreadSync) {
backgroundThreadSync.notifyAll();
}
try {
t.join();
......@@ -1997,13 +1962,12 @@ public class MVStore {
}
/**
* Set the maximum delay in milliseconds to store committed changes (for
* file-based stores).
* Set the maximum delay in milliseconds to commit changes.
* <p>
* The default is 1000, meaning committed changes are stored after at
* The default is 1000, meaning changes are committed after at
* most one second.
* <p>
* When the value is set to -1, committed changes are only written when
* When the value is set to -1, changes are only written when
* calling the store method. When the value is set to 0, committed
* changes are immediately written on a commit, but please note this
* decreases performance and does still not guarantee the disk will
......@@ -2012,6 +1976,8 @@ public class MVStore {
* @param millis the maximum delay
*/
public void setWriteDelay(int millis) {
;
// TODO rename to commitDelay
if (writeDelay == millis) {
return;
}
......@@ -2050,12 +2016,17 @@ public class MVStore {
@Override
public void run() {
while (store.backgroundThread != null) {
synchronized (store) {
while (true) {
Thread t = store.backgroundThread;
if (t == null) {
break;
}
Object sync = store.backgroundThreadSync;
synchronized (sync) {
try {
store.wait(sleep);
sync.wait(sleep);
} catch (InterruptedException e) {
// ignore
continue;
}
}
store.storeInBackground();
......@@ -2145,9 +2116,8 @@ public class MVStore {
/**
* Set the size of the write buffer, in MB (for file-based stores).
* Changes are automatically stored if the buffer grows larger than
* this. However, unless the changes are committed later on, they are
* rolled back when opening the store.
* Unless auto-commit is disabled, changes are automatically stored if
* the buffer grows larger than this.
* <p>
* The default is 4 MB.
* <p>
......
......@@ -121,8 +121,7 @@ public class OffHeapStore extends FileStore {
@Override
public void close() {
truncate(0);
freeSpace.clear();
// do nothing (keep the data until it is garbage collected)
}
@Override
......
......@@ -260,7 +260,7 @@ public class MVTableEngine implements TableEngine {
public void compactFile(long maxCompactTime) {
store.setRetentionTime(0);
long start = System.currentTimeMillis();
while (store.compact(90)) {
while (store.compact(99)) {
store.sync();
long time = System.currentTimeMillis() - start;
if (time > maxCompactTime) {
......@@ -280,7 +280,7 @@ public class MVTableEngine implements TableEngine {
public void close(long maxCompactTime) {
if (!store.isClosed() && store.getFileStore() != null) {
if (!store.getFileStore().isReadOnly()) {
store.store();
transactionStore.close();
long start = System.currentTimeMillis();
while (store.compact(90)) {
long time = System.currentTimeMillis() - start;
......
......@@ -170,8 +170,10 @@ public class TransactionStore {
public synchronized void close() {
// to avoid losing transaction ids
settings.put(LAST_TRANSACTION_ID, "" + lastTransactionId);
if (store.getFileStore() != null) {
store.store();
}
}
/**
* Begin a new transaction.
......@@ -191,7 +193,6 @@ public class TransactionStore {
private void commitIfNeeded() {
if (store.getUnsavedPageCount() > MAX_UNSAVED_PAGES) {
if (store.getFileStore() != null) {
store.commit();
store.store();
}
}
......@@ -354,10 +355,10 @@ public class TransactionStore {
firstOpenTransaction = -1;
}
if (store.getWriteDelay() == 0) {
if (store.getFileStore() == null) {
return;
if (store.getFileStore() != null) {
store.store();
}
store.commit();
return;
}
// to avoid having to store the transaction log,
// if there is no open transaction,
......@@ -1207,7 +1208,7 @@ public class TransactionStore {
*/
public Iterator<K> keyIterator(K from, boolean includeUncommitted) {
Cursor<K> it = map.keyIterator(from);
return wrapIterator(it, false);
return wrapIterator(it, includeUncommitted);
}
/**
......
......@@ -470,18 +470,18 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
prof.interval = 1;
prof.startCollecting();
if (test.mvStore) {
TestPerformance.main("-init", "-db", "9", "-size", "10000");
TestPerformance.main("-init", "-db", "9", "-size", "1000");
} else {
TestPerformance.main("-init", "-db", "1");
}
prof.stopCollecting();
System.out.println(prof.getTop(3));
System.out.println(prof.getTop(30));
if (test.mvStore) {
prof = new Profiler();
prof.depth = 16;
prof.interval = 1;
prof.startCollecting();
TestPerformance.main("-init", "-db", "1", "-size", "10000");
TestPerformance.main("-init", "-db", "1", "-size", "1000");
prof.stopCollecting();
System.out.println(prof.getTop(3));
}
......
......@@ -43,7 +43,6 @@ public class TestConcurrent extends TestMVStore {
@Override
public void test() throws Exception {
FileUtils.deleteRecursive(getBaseDir(), true);
FileUtils.createDirectories(getBaseDir());
FileUtils.deleteRecursive("memFS:", false);
......@@ -110,7 +109,7 @@ public class TestConcurrent extends TestMVStore {
m.clear();
s.removeMap(m);
if (x % 5 == 0) {
s.incrementVersion();
s.commit();
}
}
task.get();
......@@ -124,9 +123,9 @@ public class TestConcurrent extends TestMVStore {
}
}
assertEquals(1, chunkCount);
s.close();
}
FileUtils.deleteRecursive("memFS:", false);
}
private void testConcurrentStoreAndRemoveMap() throws InterruptedException {
......@@ -159,6 +158,7 @@ public class TestConcurrent extends TestMVStore {
}
task.get();
s.close();
FileUtils.deleteRecursive("memFS:", false);
}
private void testConcurrentStoreAndClose() throws InterruptedException {
......@@ -200,6 +200,7 @@ public class TestConcurrent extends TestMVStore {
}
s.close();
}
FileUtils.deleteRecursive("memFS:", false);
}
/**
......@@ -249,7 +250,7 @@ public class TestConcurrent extends TestMVStore {
}
m.get(rand.nextInt(size));
}
s.incrementVersion();
s.commit();
Thread.sleep(1);
}
task.get();
......@@ -341,7 +342,7 @@ public class TestConcurrent extends TestMVStore {
for (int k = 0; k < 10000; k++) {
Iterator<Integer> it = map.keyIterator(r.nextInt(len));
long old = s.getCurrentVersion();
s.incrementVersion();
s.commit();
s.setRetainVersion(old - 100);
while (map.getVersion() == old) {
Thread.yield();
......@@ -423,7 +424,7 @@ public class TestConcurrent extends TestMVStore {
notDetected.incrementAndGet();
}
}
s.incrementVersion();
s.commit();
Thread.sleep(1);
}
task.get();
......@@ -438,7 +439,7 @@ public class TestConcurrent extends TestMVStore {
for (int i = 0; i < size; i++) {
m.put(i, x);
}
s.incrementVersion();
s.commit();
Task task = new Task() {
@Override
public void call() throws Exception {
......@@ -462,7 +463,7 @@ public class TestConcurrent extends TestMVStore {
for (int i = 0; i < size; i++) {
m.put(i, x);
}
s.incrementVersion();
s.commit();
Thread.sleep(1);
}
task.get();
......
......@@ -148,7 +148,7 @@ public class TestMVStore extends TestBase {
s.store();
}
assertTrue(1000 < offHeap.getWriteCount());
// s.close();
s.close();
s = new MVStore.Builder().
fileStore(offHeap).
......@@ -253,7 +253,6 @@ public class TestMVStore extends TestBase {
m = s.openMap("data");
s.getFileStore().getFile().close();
m.put(1, "Hello");
s.commit();
for (int i = 0; i < 100; i++) {
if (exRef.get() != null) {
break;
......@@ -330,19 +329,19 @@ public class TestMVStore extends TestBase {
fileName(fileName).
open();
m = s.openMap("data");
assertFalse(m.containsKey(1));
assertTrue(m.containsKey(1));
m.put(1, data);
s.commit();
m.put(2, data);
m.put(-1, data);
s.store();
m.put(-2, data);
s.close();
s = new MVStore.Builder().
fileName(fileName).
open();
m = s.openMap("data");
assertTrue(m.containsKey(1));
assertFalse(m.containsKey(2));
assertTrue(m.containsKey(-1));
assertTrue(m.containsKey(-2));
s.close();
FileUtils.delete(fileName);
......@@ -377,7 +376,7 @@ public class TestMVStore extends TestBase {
m.put(1, "Hello");
s.store();
long v = s.getCurrentVersion();
m.put(2, "World");
m.put(2, "World.");
Thread.sleep(5);
// must not store, as nothing has been committed yet
s.closeImmediately();
......@@ -386,9 +385,11 @@ public class TestMVStore extends TestBase {
open();
s.setWriteDelay(1);
m = s.openMap("data");
assertEquals(null, m.get(2));
assertEquals("World.", m.get(2));
m.put(2, "World");
s.commit();
s.store();
v = s.getCurrentVersion();
m.put(3, "!");
for (int i = 100; i > 0; i--) {
......@@ -400,7 +401,7 @@ public class TestMVStore extends TestBase {
}
Thread.sleep(1);
}
s.close();
s.closeImmediately();
s = new MVStore.Builder().
fileName(fileName).
......@@ -408,7 +409,7 @@ public class TestMVStore extends TestBase {
m = s.openMap("data");
assertEquals("Hello", m.get(1));
assertEquals("World", m.get(2));
assertFalse(m.containsKey(3));
assertEquals("!", m.get(3));
s.close();
FileUtils.delete(fileName);
......@@ -517,7 +518,7 @@ public class TestMVStore extends TestBase {
MVMap<Integer, Integer> map;
map = s.openMap("hello");
map.put(1, 10);
long old = s.incrementVersion();
long old = s.commit();
s.renameMap(map, "world");
map.put(2, 20);
assertEquals("world", map.getName());
......@@ -557,7 +558,7 @@ public class TestMVStore extends TestBase {
s.store();
s.close();
int[] expectedReadsForCacheSize = {
3405, 2590, 1924, 1440, 1103, 956, 918
3405, 2590, 1924, 1440, 1108, 956, 918
};
for (int cacheSize = 0; cacheSize <= 6; cacheSize += 4) {
s = new MVStore.Builder().
......@@ -816,8 +817,10 @@ public class TestMVStore extends TestBase {
MVStore s = MVStore.open(fileName);
assertEquals(0, s.getCurrentVersion());
assertEquals(0, s.getStoreVersion());
s.setStoreVersion(0);
s.store();
s.setStoreVersion(1);
s.close();
s.closeImmediately();
s = MVStore.open(fileName);
assertEquals(1, s.getCurrentVersion());
assertEquals(0, s.getStoreVersion());
......@@ -840,7 +843,7 @@ public class TestMVStore extends TestBase {
map.put(i, 10 * i);
}
Iterator<Integer> it = map.keySet().iterator();
s.incrementVersion();
s.commit();
for (int i = 0; i < len; i += 2) {
map.remove(i);
}
......@@ -916,7 +919,7 @@ public class TestMVStore extends TestBase {
long oldVersion = s.getCurrentVersion();
// from now on, the old version is read-only
s.incrementVersion();
s.commit();
// more changes, in the new version
// changes can be rolled back if required
......@@ -976,7 +979,7 @@ public class TestMVStore extends TestBase {
if (op == 1) {
m.put("1", "" + s.getCurrentVersion());
}
s.incrementVersion();
s.commit();
}
for (int j = 0; j < s.getCurrentVersion(); j++) {
MVMap<String, String> old = m.openVersion(j);
......@@ -997,14 +1000,14 @@ public class TestMVStore extends TestBase {
MVMap<String, String> m;
m = s.openMap("data");
long first = s.getCurrentVersion();
s.incrementVersion();
s.commit();
m.put("1", "Hello");
m.put("2", "World");
for (int i = 10; i < 20; i++) {
m.put("" + i, "data");
}
long old = s.getCurrentVersion();
s.incrementVersion();
s.commit();
m.put("1", "Hallo");
m.put("2", "Welt");
MVMap<String, String> mFirst;
......@@ -1119,7 +1122,7 @@ public class TestMVStore extends TestBase {
assertTrue(s.hasUnsavedChanges());
MVMap<String, String> m0 = s.openMap("data0");
m.put("1", "Hello");
assertEquals(1, s.incrementVersion());
assertEquals(1, s.commit());
s.rollbackTo(1);
assertEquals(1, s.getCurrentVersion());
assertEquals("Hello", m.get("1"));
......@@ -1167,16 +1170,13 @@ public class TestMVStore extends TestBase {
assertEquals("Hello", m.get("1"));
assertFalse(m0.isReadOnly());
m.put("1", "Hallo");
s.incrementVersion();
s.commit();
long v3 = s.getCurrentVersion();
assertEquals(3, v3);
long v4 = s.store();
assertEquals(4, v4);
assertEquals(4, s.getCurrentVersion());
s.close();
s = openStore(fileName);
assertEquals(4, s.getCurrentVersion());
assertEquals(3, s.getCurrentVersion());
m = s.openMap("data");
m.put("1", "Hi");
s.store();
......@@ -1185,7 +1185,7 @@ public class TestMVStore extends TestBase {
s = openStore(fileName);
m = s.openMap("data");
assertEquals("Hi", m.get("1"));
s.rollbackTo(v4);
s.rollbackTo(v3);
assertEquals("Hallo", m.get("1"));
s.close();
......@@ -1212,7 +1212,7 @@ public class TestMVStore extends TestBase {
for (int i = 0; i < 10; i++) {
m2.put("" + i, "Test");
}
long v1 = s.incrementVersion();
long v1 = s.commit();
assertEquals(1, v1);
assertEquals(1, s.getCurrentVersion());
MVMap<String, String> m1 = s.openMap("data1");
......
......@@ -73,7 +73,7 @@ public class TestMVStoreBenchmark extends TestBase {
}
private static long[] getMemoryUsed(int count, int size) {
private long[] getMemoryUsed(int count, int size) {
long hash, tree, mv;
ArrayList<Map<Integer, String>> mapList;
long mem;
......@@ -107,6 +107,10 @@ public class TestMVStoreBenchmark extends TestBase {
mv = getMemory() - mem;
mapList.size();
trace("hash: " + hash / 1024 / 1024 + " mb");
trace("tree: " + tree / 1024 / 1024 + " mb");
trace("mv: " + mv / 1024 / 1024 + " mb");
return new long[]{hash, tree, mv};
}
......@@ -179,7 +183,7 @@ public class TestMVStoreBenchmark extends TestBase {
}
time = System.currentTimeMillis() - time;
}
// System.out.println(map.getClass().getName() + ": " + time);
trace(map.getClass().getName() + ": " + time);
return time;
}
......
......@@ -141,7 +141,7 @@ public class TestMVTableEngine extends TestBase {
conn.close();
long sizeNew = FileUtils.size(getBaseDir() + "/mvstore"
+ Constants.SUFFIX_MV_FILE);
assertTrue(sizeNew < sizeOld);
assertTrue("new: " + sizeNew + " old: " + sizeOld, sizeNew < sizeOld);
}
private void testTwoPhaseCommit() throws Exception {
......
......@@ -59,6 +59,7 @@ public class TestTransactionStore extends TestBase {
private void testStopWhileCommitting() throws Exception {
String fileName = getBaseDir() + "/testStopWhileCommitting.h3";
FileUtils.delete(fileName);
Random r = new Random(0);
for (int i = 0; i < 10;) {
MVStore s;
......@@ -100,6 +101,16 @@ public class TestTransactionStore extends TestBase {
task.get();
store.close();
s = MVStore.open(fileName);
// roll back a bit, until we have some undo log entries
assertTrue(s.hasMap("undoLog"));
for (int back = 0; back < 100; back++) {
int minus = r.nextInt(10);
s.rollbackTo(Math.max(0, s.getCurrentVersion() - minus));
MVMap<?, ?> undo = s.openMap("undoLog");
if (undo.size() > 0) {
break;
}
}
ts = new TransactionStore(s);
List<Transaction> list = ts.getOpenTransactions();
if (list.size() != 0) {
......@@ -111,10 +122,6 @@ public class TestTransactionStore extends TestBase {
s.close();
FileUtils.delete(fileName);
assertFalse(FileUtils.exists(fileName));
FileUtils.delete(fileName);
assertFalse(FileUtils.exists(fileName));
s.close();
FileUtils.delete(fileName);
}
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论