提交 0be646df authored 作者: Thomas Mueller's avatar Thomas Mueller

New simulated power-off test

上级 ff8b9bc7
/*
* Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.constant.ErrorCode;
import org.h2.store.fs.FileSystem;
import org.h2.test.TestBase;
import org.h2.test.utils.FileSystemDebug;
/**
* Tests that use the debug file system to simulate power failure.
*/
public class TestPowerOffFs extends TestBase {
private FileSystemDebug fs;
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
public void test() throws Exception {
int todo;
FileSystemDebug.register();
fs = (FileSystemDebug) FileSystem.getInstance("debug:/");
test(Integer.MAX_VALUE);
System.out.println(Integer.MAX_VALUE - fs.getPowerOffCount());
System.out.println("done");
for (int i = 0;; i++) {
boolean end = test(i);
if (end) {
break;
}
}
}
private boolean test(int x) throws SQLException {
deleteDb("memFS:", null);
fs.setPowerOffCount(x);
String url = "jdbc:h2:debug:memFS:powerOffFs;FILE_LOCK=NO;TRACE_LEVEL_FILE=0;WRITE_DELAY=0;CACHE_SIZE=4096";
url += ";page_store=true";
Connection conn = null;
Statement stat = null;
if (x == 16) {
System.out.println("x");
}
try {
conn = DriverManager.getConnection(url);
stat = conn.createStatement();
stat.execute("create table test(id int primary key, name varchar)");
stat.execute("insert into test values(1, 'Hello')");
stat.execute("create index idx_name on test(name)");
stat.execute("insert into test values(2, 'World')");
stat.execute("update test set name='Hallo' where id=1");
stat.execute("delete from test where name=2");
stat.execute("insert into test values(3, space(10000))");
stat.execute("update test set name='Hallo' where id=3");
stat.execute("drop table test");
conn.close();
conn = null;
return fs.getPowerOffCount() > 0;
} catch (SQLException e) {
if (e.getErrorCode() == ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1) {
throw e;
}
// ignore
} finally {
if (conn != null) {
try {
if (stat != null) {
stat.execute("shutdown immediately");
}
} catch (Exception e2) {
// ignore
}
try {
conn.close();
} catch (Exception e2) {
// ignore
}
}
}
fs.setPowerOffCount(0);
conn = DriverManager.getConnection(url);
stat = conn.createStatement();
stat.execute("script to 'memFS:test.sql'");
conn.close();
return false;
}
}
...@@ -55,6 +55,7 @@ public class FileObjectDebug implements FileObject { ...@@ -55,6 +55,7 @@ public class FileObjectDebug implements FileObject {
} }
public void setFileLength(long newLength) throws IOException { public void setFileLength(long newLength) throws IOException {
checkPowerOff();
debug("setFileLength", newLength); debug("setFileLength", newLength);
file.setFileLength(newLength); file.setFileLength(newLength);
} }
...@@ -65,12 +66,25 @@ public class FileObjectDebug implements FileObject { ...@@ -65,12 +66,25 @@ public class FileObjectDebug implements FileObject {
} }
public void write(byte[] b, int off, int len) throws IOException { public void write(byte[] b, int off, int len) throws IOException {
checkPowerOff();
debug("write", off, len); debug("write", off, len);
file.write(b, off, len); file.write(b, off, len);
} }
private void debug(String method, Object... params) { private void debug(String method, Object... params) {
fs.debug(method, name, params); fs.trace(method, name, params);
} }
private void checkPowerOff() throws IOException {
try {
fs.checkPowerOff();
} catch (IOException e) {
try {
file.close();
} catch (IOException e2) {
// ignore
}
throw e;
}
}
} }
...@@ -26,6 +26,11 @@ public class FileSystemDebug extends FileSystem { ...@@ -26,6 +26,11 @@ public class FileSystemDebug extends FileSystem {
private static final FileSystemDebug INSTANCE = new FileSystemDebug(); private static final FileSystemDebug INSTANCE = new FileSystemDebug();
private static final IOException POWER_OFF = new IOException("Simulated power failure");
private int powerOffCount;
private boolean trace;
/** /**
* Register the file system. * Register the file system.
*/ */
...@@ -33,114 +38,133 @@ public class FileSystemDebug extends FileSystem { ...@@ -33,114 +38,133 @@ public class FileSystemDebug extends FileSystem {
FileSystem.register(INSTANCE); FileSystem.register(INSTANCE);
} }
/**
* Check if the simulated power failure occurred.
* This call will decrement the countdown.
*
* @throws IOException if the simulated power failure occurred
*/
void checkPowerOff() throws IOException {
if (powerOffCount == 0) {
return;
}
if (powerOffCount > 1) {
powerOffCount--;
return;
}
powerOffCount = -1;
// throw new IOException("Simulated power failure");
throw POWER_OFF;
}
public boolean canWrite(String fileName) { public boolean canWrite(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("canWrite", fileName); trace("canWrite", fileName);
return FileSystem.getInstance(fileName).canWrite(fileName); return FileSystem.getInstance(fileName).canWrite(fileName);
} }
public void copy(String original, String copy) throws SQLException { public void copy(String original, String copy) throws SQLException {
original = translateFileName(original); original = translateFileName(original);
copy = translateFileName(copy); copy = translateFileName(copy);
debug("copy", original, copy); trace("copy", original, copy);
FileSystem.getInstance(original).copy(original, copy); FileSystem.getInstance(original).copy(original, copy);
} }
public void createDirs(String fileName) throws SQLException { public void createDirs(String fileName) throws SQLException {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("createDirs", fileName); trace("createDirs", fileName);
FileSystem.getInstance(fileName).createDirs(fileName); FileSystem.getInstance(fileName).createDirs(fileName);
} }
public boolean createNewFile(String fileName) throws SQLException { public boolean createNewFile(String fileName) throws SQLException {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("createNewFile", fileName); trace("createNewFile", fileName);
return FileSystem.getInstance(fileName).createNewFile(fileName); return FileSystem.getInstance(fileName).createNewFile(fileName);
} }
public String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir) public String createTempFile(String prefix, String suffix, boolean deleteOnExit, boolean inTempDir)
throws IOException { throws IOException {
prefix = translateFileName(prefix); prefix = translateFileName(prefix);
debug("createTempFile", prefix, suffix, deleteOnExit, inTempDir); trace("createTempFile", prefix, suffix, deleteOnExit, inTempDir);
return PREFIX + FileSystem.getInstance(prefix).createTempFile(prefix, suffix, deleteOnExit, inTempDir); return PREFIX + FileSystem.getInstance(prefix).createTempFile(prefix, suffix, deleteOnExit, inTempDir);
} }
public void delete(String fileName) throws SQLException { public void delete(String fileName) throws SQLException {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("fileName", fileName); trace("fileName", fileName);
FileSystem.getInstance(fileName).delete(fileName); FileSystem.getInstance(fileName).delete(fileName);
} }
public void deleteRecursive(String directory, boolean tryOnly) throws SQLException { public void deleteRecursive(String directory, boolean tryOnly) throws SQLException {
directory = translateFileName(directory); directory = translateFileName(directory);
debug("deleteRecursive", directory); trace("deleteRecursive", directory);
FileSystem.getInstance(directory).deleteRecursive(directory, tryOnly); FileSystem.getInstance(directory).deleteRecursive(directory, tryOnly);
} }
public boolean exists(String fileName) { public boolean exists(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("exists", fileName); trace("exists", fileName);
return FileSystem.getInstance(fileName).exists(fileName); return FileSystem.getInstance(fileName).exists(fileName);
} }
public boolean fileStartsWith(String fileName, String prefix) { public boolean fileStartsWith(String fileName, String prefix) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
prefix = translateFileName(prefix); prefix = translateFileName(prefix);
debug("fileStartsWith", fileName, prefix); trace("fileStartsWith", fileName, prefix);
return FileSystem.getInstance(fileName).fileStartsWith(fileName, prefix); return FileSystem.getInstance(fileName).fileStartsWith(fileName, prefix);
} }
public String getAbsolutePath(String fileName) { public String getAbsolutePath(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("getAbsolutePath", fileName); trace("getAbsolutePath", fileName);
return PREFIX + FileSystem.getInstance(fileName).getAbsolutePath(fileName); return PREFIX + FileSystem.getInstance(fileName).getAbsolutePath(fileName);
} }
public String getFileName(String name) throws SQLException { public String getFileName(String name) throws SQLException {
name = translateFileName(name); name = translateFileName(name);
debug("getFileName", name); trace("getFileName", name);
return FileSystem.getInstance(name).getFileName(name); return FileSystem.getInstance(name).getFileName(name);
} }
public long getLastModified(String fileName) { public long getLastModified(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("getLastModified", fileName); trace("getLastModified", fileName);
return FileSystem.getInstance(fileName).getLastModified(fileName); return FileSystem.getInstance(fileName).getLastModified(fileName);
} }
public String getParent(String fileName) { public String getParent(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("getParent", fileName); trace("getParent", fileName);
return PREFIX + FileSystem.getInstance(fileName).getParent(fileName); return PREFIX + FileSystem.getInstance(fileName).getParent(fileName);
} }
public boolean isAbsolute(String fileName) { public boolean isAbsolute(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("isAbsolute", fileName); trace("isAbsolute", fileName);
return FileSystem.getInstance(fileName).isAbsolute(fileName); return FileSystem.getInstance(fileName).isAbsolute(fileName);
} }
public boolean isDirectory(String fileName) { public boolean isDirectory(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("isDirectory", fileName); trace("isDirectory", fileName);
return FileSystem.getInstance(fileName).isDirectory(fileName); return FileSystem.getInstance(fileName).isDirectory(fileName);
} }
public boolean isReadOnly(String fileName) { public boolean isReadOnly(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("isReadOnly", fileName); trace("isReadOnly", fileName);
return FileSystem.getInstance(fileName).isReadOnly(fileName); return FileSystem.getInstance(fileName).isReadOnly(fileName);
} }
public long length(String fileName) { public long length(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("length", fileName); trace("length", fileName);
return FileSystem.getInstance(fileName).length(fileName); return FileSystem.getInstance(fileName).length(fileName);
} }
public String[] listFiles(String directory) throws SQLException { public String[] listFiles(String directory) throws SQLException {
directory = translateFileName(directory); directory = translateFileName(directory);
debug("listFiles", directory); trace("listFiles", directory);
String[] list = FileSystem.getInstance(directory).listFiles(directory); String[] list = FileSystem.getInstance(directory).listFiles(directory);
for (int i = 0; i < list.length; i++) { for (int i = 0; i < list.length; i++) {
list[i] = PREFIX + list[i]; list[i] = PREFIX + list[i];
...@@ -150,38 +174,38 @@ public class FileSystemDebug extends FileSystem { ...@@ -150,38 +174,38 @@ public class FileSystemDebug extends FileSystem {
public String normalize(String fileName) throws SQLException { public String normalize(String fileName) throws SQLException {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("normalize", fileName); trace("normalize", fileName);
return PREFIX + FileSystem.getInstance(fileName).normalize(fileName); return PREFIX + FileSystem.getInstance(fileName).normalize(fileName);
} }
public InputStream openFileInputStream(String fileName) throws IOException { public InputStream openFileInputStream(String fileName) throws IOException {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("openFileInputStream", fileName); trace("openFileInputStream", fileName);
return FileSystem.getInstance(fileName).openFileInputStream(fileName); return FileSystem.getInstance(fileName).openFileInputStream(fileName);
} }
public FileObject openFileObject(String fileName, String mode) throws IOException { public FileObject openFileObject(String fileName, String mode) throws IOException {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("openFileObject", fileName, mode); trace("openFileObject", fileName, mode);
return new FileObjectDebug(this, FileSystem.getInstance(fileName).openFileObject(fileName, mode)); return new FileObjectDebug(this, FileSystem.getInstance(fileName).openFileObject(fileName, mode));
} }
public OutputStream openFileOutputStream(String fileName, boolean append) throws SQLException { public OutputStream openFileOutputStream(String fileName, boolean append) throws SQLException {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("openFileOutputStream", fileName, append); trace("openFileOutputStream", fileName, append);
return FileSystem.getInstance(fileName).openFileOutputStream(fileName, append); return FileSystem.getInstance(fileName).openFileOutputStream(fileName, append);
} }
public void rename(String oldName, String newName) throws SQLException { public void rename(String oldName, String newName) throws SQLException {
oldName = translateFileName(oldName); oldName = translateFileName(oldName);
newName = translateFileName(newName); newName = translateFileName(newName);
debug("rename", oldName, newName); trace("rename", oldName, newName);
FileSystem.getInstance(oldName).copy(oldName, newName); FileSystem.getInstance(oldName).copy(oldName, newName);
} }
public boolean tryDelete(String fileName) { public boolean tryDelete(String fileName) {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
debug("tryDelete", fileName); trace("tryDelete", fileName);
return FileSystem.getInstance(fileName).tryDelete(fileName); return FileSystem.getInstance(fileName).tryDelete(fileName);
} }
...@@ -203,7 +227,8 @@ public class FileSystemDebug extends FileSystem { ...@@ -203,7 +227,8 @@ public class FileSystemDebug extends FileSystem {
* @param fileName the file name * @param fileName the file name
* @param params parameters if any * @param params parameters if any
*/ */
void debug(String method, String fileName, Object... params) { void trace(String method, String fileName, Object... params) {
if (trace) {
StringBuilder buff = new StringBuilder(" "); StringBuilder buff = new StringBuilder(" ");
buff.append(fileName).append(' ').append(method); buff.append(fileName).append(' ').append(method);
for (Object s : params) { for (Object s : params) {
...@@ -211,5 +236,22 @@ public class FileSystemDebug extends FileSystem { ...@@ -211,5 +236,22 @@ public class FileSystemDebug extends FileSystem {
} }
System.out.println(buff); System.out.println(buff);
} }
}
public void setPowerOffCount(int count) {
this.powerOffCount = count;
}
public int getPowerOffCount() {
return powerOffCount;
}
public boolean isTrace() {
return trace;
}
public void setTrace(boolean trace) {
this.trace = trace;
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论