提交 152f7ba7 authored 作者: Thomas Mueller's avatar Thomas Mueller

MVTableEngine: improve tests

上级 24505f0e
...@@ -383,8 +383,13 @@ meaning outside of the regular garbage collected heap. This allows to use very l ...@@ -383,8 +383,13 @@ meaning outside of the regular garbage collected heap. This allows to use very l
stores without having to increase the JVM heap (which would increase Java garbage collection stores without having to increase the JVM heap (which would increase Java garbage collection
pauses a lot). Memory is allocated using <code>ByteBuffer.allocateDirect</code>. pauses a lot). Memory is allocated using <code>ByteBuffer.allocateDirect</code>.
One chunk is allocated at a time (each chunk is usually a few MB large), so that One chunk is allocated at a time (each chunk is usually a few MB large), so that
allocation cost is low. allocation cost is low. To use the off-heap storage, call:
</p> </p>
<pre>
OffHeapStore offHeap = new OffHeapStore();
MVStore s = new MVStore.Builder().
fileStore(offHeap).open();
</pre>
<h3 id="fileSystem">File System Abstraction, File Locking and Online Backup</h3> <h3 id="fileSystem">File System Abstraction, File Locking and Online Backup</h3>
<p> <p>
......
...@@ -110,6 +110,8 @@ MVStore: ...@@ -110,6 +110,8 @@ MVStore:
- add new feature to file systems that avoid copying data - add new feature to file systems that avoid copying data
(reads should return a ByteBuffer, not write into one) (reads should return a ByteBuffer, not write into one)
- do we need to store a dummy chunk entry in the chunk itself? - do we need to store a dummy chunk entry in the chunk itself?
currently yes, as some fields are not set in the chunk header
- off-heap LIRS cache (the LIRS cache should use a map factory)
*/ */
......
...@@ -30,6 +30,7 @@ import org.h2.util.New; ...@@ -30,6 +30,7 @@ import org.h2.util.New;
public class FilePathMem extends FilePath { public class FilePathMem extends FilePath {
private static final TreeMap<String, FileMemData> MEMORY_FILES = new TreeMap<String, FileMemData>(); private static final TreeMap<String, FileMemData> MEMORY_FILES = new TreeMap<String, FileMemData>();
private static final FileMemData DIRECTORY = new FileMemData("", false);
@Override @Override
public FilePathMem getPath(String path) { public FilePathMem getPath(String path) {
...@@ -90,7 +91,9 @@ public class FilePathMem extends FilePath { ...@@ -90,7 +91,9 @@ public class FilePathMem extends FilePath {
synchronized (MEMORY_FILES) { synchronized (MEMORY_FILES) {
for (String n : MEMORY_FILES.tailMap(name).keySet()) { for (String n : MEMORY_FILES.tailMap(name).keySet()) {
if (n.startsWith(name)) { if (n.startsWith(name)) {
if (!n.equals(name) && n.indexOf('/', name.length() + 1) < 0) {
list.add(getPath(n)); list.add(getPath(n));
}
} else { } else {
break; break;
} }
...@@ -120,10 +123,9 @@ public class FilePathMem extends FilePath { ...@@ -120,10 +123,9 @@ public class FilePathMem extends FilePath {
if (isRoot()) { if (isRoot()) {
return true; return true;
} }
// TODO in memory file system currently
// does not really support directories
synchronized (MEMORY_FILES) { synchronized (MEMORY_FILES) {
return MEMORY_FILES.get(name) == null; FileMemData d = MEMORY_FILES.get(name);
return d == DIRECTORY;
} }
} }
...@@ -145,10 +147,12 @@ public class FilePathMem extends FilePath { ...@@ -145,10 +147,12 @@ public class FilePathMem extends FilePath {
@Override @Override
public void createDirectory() { public void createDirectory() {
if (exists() && isDirectory()) { if (exists()) {
throw DbException.get(ErrorCode.FILE_CREATION_FAILED_1, name + " (a file with this name already exists)"); throw DbException.get(ErrorCode.FILE_CREATION_FAILED_1, name + " (a file with this name already exists)");
} }
// TODO directories are not really supported synchronized (MEMORY_FILES) {
MEMORY_FILES.put(name, DIRECTORY);
}
} }
@Override @Override
...@@ -174,6 +178,9 @@ public class FilePathMem extends FilePath { ...@@ -174,6 +178,9 @@ public class FilePathMem extends FilePath {
private FileMemData getMemoryFile() { private FileMemData getMemoryFile() {
synchronized (MEMORY_FILES) { synchronized (MEMORY_FILES) {
FileMemData m = MEMORY_FILES.get(name); FileMemData m = MEMORY_FILES.get(name);
if (m == DIRECTORY) {
throw DbException.get(ErrorCode.FILE_CREATION_FAILED_1, name + " (a directory with this name already exists)");
}
if (m == null) { if (m == null) {
m = new FileMemData(name, compressed()); m = new FileMemData(name, compressed());
MEMORY_FILES.put(name, m); MEMORY_FILES.put(name, m);
......
...@@ -154,6 +154,11 @@ class FileRec extends FileBase { ...@@ -154,6 +154,11 @@ class FileRec extends FileBase {
return channel.read(dst); return channel.read(dst);
} }
@Override
public int read(ByteBuffer dst, long position) throws IOException {
return channel.read(dst, position);
}
@Override @Override
public FileChannel position(long pos) throws IOException { public FileChannel position(long pos) throws IOException {
channel.position(pos); channel.position(pos);
...@@ -186,6 +191,20 @@ class FileRec extends FileBase { ...@@ -186,6 +191,20 @@ class FileRec extends FileBase {
return result; return result;
} }
@Override
public int write(ByteBuffer src, long position) throws IOException {
byte[] buff = src.array();
int len = src.remaining();
if (src.position() != 0 || len != buff.length) {
byte[] b = new byte[len];
System.arraycopy(buff, src.arrayOffset() + src.position(), b, 0, len);
buff = b;
}
int result = channel.write(src, position);
rec.log(Recorder.WRITE, name, buff, position);
return result;
}
@Override @Override
public synchronized FileLock tryLock(long position, long size, boolean shared) throws IOException { public synchronized FileLock tryLock(long position, long size, boolean shared) throws IOException {
return channel.tryLock(position, size, shared); return channel.tryLock(position, size, shared);
......
...@@ -57,6 +57,7 @@ public class TestFullText extends TestBase { ...@@ -57,6 +57,7 @@ public class TestFullText extends TestBase {
testPerformance(false); testPerformance(false);
testReopen(false); testReopen(false);
testDropIndex(false); testDropIndex(false);
if (!config.reopen) {
try { try {
Class.forName(LUCENE_FULLTEXT_CLASS_NAME); Class.forName(LUCENE_FULLTEXT_CLASS_NAME);
testCreateDropLucene(); testCreateDropLucene();
...@@ -77,6 +78,7 @@ public class TestFullText extends TestBase { ...@@ -77,6 +78,7 @@ public class TestFullText extends TestBase {
// ok // ok
} }
FullText.closeAll(); FullText.closeAll();
}
deleteDb("fullText"); deleteDb("fullText");
deleteDb("fullTextReopen"); deleteDb("fullTextReopen");
} }
......
...@@ -9,7 +9,6 @@ import java.util.Random; ...@@ -9,7 +9,6 @@ import java.util.Random;
import org.h2.mvstore.MVMap; import org.h2.mvstore.MVMap;
import org.h2.mvstore.MVStore; import org.h2.mvstore.MVStore;
import org.h2.store.fs.FilePathCrypt;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.test.utils.FilePathUnstable; import org.h2.test.utils.FilePathUnstable;
...@@ -29,28 +28,33 @@ public class TestKillProcessWhileWriting extends TestBase { ...@@ -29,28 +28,33 @@ public class TestKillProcessWhileWriting extends TestBase {
* @param a ignored * @param a ignored
*/ */
public static void main(String... a) throws Exception { public static void main(String... a) throws Exception {
TestBase.createCaller().init().test(); TestBase test = TestBase.createCaller().init();
test.config.big = true;
test.test();
} }
@Override @Override
public void test() throws Exception { public void test() throws Exception {
fs = FilePathUnstable.register(); fs = FilePathUnstable.register();
fs.setPartialWrites(false);
test("unstable:memFS:killProcess.h3"); test("unstable:memFS:killProcess.h3");
int todo; if (config.big) {
// need to test with a file system splits writes into blocks of 4 KB fs.setPartialWrites(true);
FilePathCrypt.register(); test("unstable:memFS:killProcess.h3");
test("unstable:crypt:0007:memFS:killProcess.h3"); }
} }
private void test(String fileName) throws Exception { private void test(String fileName) throws Exception {
for (seed = 0; seed < 10; seed++) { for (seed = 0; seed < 10; seed++) {
this.fileName = fileName; this.fileName = fileName;
fs.setSeed(seed);
FileUtils.delete(fileName); FileUtils.delete(fileName);
test(Integer.MAX_VALUE); test(Integer.MAX_VALUE);
int max = Integer.MAX_VALUE - fs.getDiskFullCount() + 10; int max = Integer.MAX_VALUE - fs.getDiskFullCount() + 10;
assertTrue("" + (max - 10), max > 0); assertTrue("" + (max - 10), max > 0);
for (int i = 0; i < max; i++) { for (int i = 0; i < max; i++) {
fs.setSeed(seed);
test(i); test(i);
} }
} }
......
...@@ -71,7 +71,8 @@ public class TestReopen extends TestBase implements Recorder { ...@@ -71,7 +71,8 @@ public class TestReopen extends TestBase implements Recorder {
if (op != Recorder.WRITE && op != Recorder.TRUNCATE) { if (op != Recorder.WRITE && op != Recorder.TRUNCATE) {
return; return;
} }
if (!fileName.endsWith(Constants.SUFFIX_PAGE_FILE)) { if (!fileName.endsWith(Constants.SUFFIX_PAGE_FILE) &&
!fileName.endsWith(Constants.SUFFIX_MV_FILE)) {
return; return;
} }
if (testing) { if (testing) {
...@@ -98,14 +99,22 @@ public class TestReopen extends TestBase implements Recorder { ...@@ -98,14 +99,22 @@ public class TestReopen extends TestBase implements Recorder {
System.out.println("+ write #" + writeCount + " verify #" + verifyCount); System.out.println("+ write #" + writeCount + " verify #" + verifyCount);
try { try {
if (fileName.endsWith(Constants.SUFFIX_PAGE_FILE)) {
IOUtils.copyFiles(fileName, testDatabase + Constants.SUFFIX_PAGE_FILE); IOUtils.copyFiles(fileName, testDatabase + Constants.SUFFIX_PAGE_FILE);
} else {
IOUtils.copyFiles(fileName, testDatabase + Constants.SUFFIX_MV_FILE);
}
verifyCount++; verifyCount++;
// avoid using the Engine class to avoid deadlocks // avoid using the Engine class to avoid deadlocks
Properties p = new Properties(); Properties p = new Properties();
String userName = getUser(); String userName = getUser();
p.setProperty("user", userName); p.setProperty("user", userName);
p.setProperty("password", getPassword()); p.setProperty("password", getPassword());
ConnectionInfo ci = new ConnectionInfo("jdbc:h2:" + testDatabase + ";FILE_LOCK=NO;TRACE_LEVEL_FILE=0", p); String url = "jdbc:h2:" + testDatabase + ";FILE_LOCK=NO;TRACE_LEVEL_FILE=0";
if (config.mvStore) {
url += ";MV_STORE=TRUE";
}
ConnectionInfo ci = new ConnectionInfo(url, p);
Database database = new Database(ci, null); Database database = new Database(ci, null);
// close the database // close the database
Session session = database.getSystemSession(); Session session = database.getSystemSession();
...@@ -145,10 +154,18 @@ public class TestReopen extends TestBase implements Recorder { ...@@ -145,10 +154,18 @@ public class TestReopen extends TestBase implements Recorder {
} }
testDatabase += "X"; testDatabase += "X";
try { try {
if (fileName.endsWith(Constants.SUFFIX_PAGE_FILE)) {
IOUtils.copyFiles(fileName, testDatabase + Constants.SUFFIX_PAGE_FILE); IOUtils.copyFiles(fileName, testDatabase + Constants.SUFFIX_PAGE_FILE);
} else {
IOUtils.copyFiles(fileName, testDatabase + Constants.SUFFIX_MV_FILE);
}
// avoid using the Engine class to avoid deadlocks // avoid using the Engine class to avoid deadlocks
Properties p = new Properties(); Properties p = new Properties();
ConnectionInfo ci = new ConnectionInfo("jdbc:h2:" + testDatabase + ";FILE_LOCK=NO", p); String url = "jdbc:h2:" + testDatabase + ";FILE_LOCK=NO";
if (config.mvStore) {
url += ";MV_STORE=TRUE";
}
ConnectionInfo ci = new ConnectionInfo(url, p);
Database database = new Database(ci, null); Database database = new Database(ci, null);
// close the database // close the database
database.removeSession(null); database.removeSession(null);
......
...@@ -13,6 +13,8 @@ import java.nio.ByteBuffer; ...@@ -13,6 +13,8 @@ import java.nio.ByteBuffer;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.channels.FileLock; import java.nio.channels.FileLock;
import java.util.List; import java.util.List;
import java.util.Random;
import org.h2.store.fs.FileBase; import org.h2.store.fs.FileBase;
import org.h2.store.fs.FilePath; import org.h2.store.fs.FilePath;
import org.h2.store.fs.FilePathWrapper; import org.h2.store.fs.FilePathWrapper;
...@@ -29,6 +31,10 @@ public class FilePathUnstable extends FilePathWrapper { ...@@ -29,6 +31,10 @@ public class FilePathUnstable extends FilePathWrapper {
private static int diskFullOffCount; private static int diskFullOffCount;
private static boolean partialWrites;
private static Random random = new Random(1);
/** /**
* Register the file system. * Register the file system.
* *
...@@ -39,6 +45,42 @@ public class FilePathUnstable extends FilePathWrapper { ...@@ -39,6 +45,42 @@ public class FilePathUnstable extends FilePathWrapper {
return INSTANCE; return INSTANCE;
} }
/**
* Whether partial writes are possible (writing only part of the data).
*
* @param partialWrites true to enable
*/
public void setPartialWrites(boolean partialWrites) {
FilePathUnstable.partialWrites = partialWrites;
}
boolean getPartialWrites() {
return partialWrites;
}
/**
* Set the random seed.
*
* @param seed the new seed
*/
public void setSeed(long seed) {
random.setSeed(seed);
}
/**
* Get a buffer with a subset (the head) of the data of the source buffer.
*
* @param src the source buffer
* @return a buffer with a subset of the data
*/
ByteBuffer getRandomSubset(ByteBuffer src) {
int len = src.remaining();
len = Math.min(4096, Math.min(len, 1 + random.nextInt(len)));
ByteBuffer temp = ByteBuffer.allocate(len);
src.get(temp.array());
return temp;
}
/** /**
* Check if the simulated problem occurred. * Check if the simulated problem occurred.
* This call will decrement the countdown. * This call will decrement the countdown.
...@@ -52,7 +94,7 @@ public class FilePathUnstable extends FilePathWrapper { ...@@ -52,7 +94,7 @@ public class FilePathUnstable extends FilePathWrapper {
if (--diskFullOffCount > 0) { if (--diskFullOffCount > 0) {
return; return;
} }
if (diskFullOffCount >= -4) { if (diskFullOffCount >= -1) {
diskFullOffCount--; diskFullOffCount--;
throw DISK_FULL; throw DISK_FULL;
} }
...@@ -231,12 +273,18 @@ class FileUnstable extends FileBase { ...@@ -231,12 +273,18 @@ class FileUnstable extends FileBase {
@Override @Override
public int write(ByteBuffer src) throws IOException { public int write(ByteBuffer src) throws IOException {
checkError(); checkError();
if (file.getPartialWrites()) {
return channel.write(file.getRandomSubset(src));
}
return channel.write(src); return channel.write(src);
} }
@Override @Override
public int write(ByteBuffer src, long position) throws IOException { public int write(ByteBuffer src, long position) throws IOException {
checkError(); checkError();
if (file.getPartialWrites()) {
return channel.write(file.getRandomSubset(src), position);
}
return channel.write(src, position); return channel.write(src, position);
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论