提交 03831ec1 authored 作者: Thomas Mueller's avatar Thomas Mueller

When the shutdown hook closed the database, the last log file was deleted too early.

上级 4d0dc0c8
...@@ -18,7 +18,9 @@ Change Log ...@@ -18,7 +18,9 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>JdbcConnectionPool: it was possible to set a negative connection pool size. <ul><li>When the shutdown hook closed the database, the last log file
was deleted too early. This could cause uncommitted changes to be persisted.
</li><li>JdbcConnectionPool: it was possible to set a negative connection pool size.
</li><li>Fulltext search did not support table names with a backslash. </li><li>Fulltext search did not support table names with a backslash.
</li><li>The internal IntArray class did not work correctly when initialized with a zero length array. </li><li>The internal IntArray class did not work correctly when initialized with a zero length array.
</li><li>The H2 Console web application (war file) did only support ASCII characters. </li><li>The H2 Console web application (war file) did only support ASCII characters.
......
...@@ -1054,6 +1054,9 @@ public class Database implements DataHandler { ...@@ -1054,6 +1054,9 @@ public class Database implements DataHandler {
for (int i = 0; i < all.length; i++) { for (int i = 0; i < all.length; i++) {
Session s = all[i]; Session s = all[i];
try { try {
// must roll back, otherwise the session is removed and
// the log file that contains its uncommitted operations as well
s.rollback();
s.close(); s.close();
} catch (SQLException e) { } catch (SQLException e) {
traceSystem.getTrace(Trace.SESSION).error("disconnecting #" + s.getId(), e); traceSystem.getTrace(Trace.SESSION).error("disconnecting #" + s.getId(), e);
......
...@@ -144,6 +144,9 @@ public class LogSystem { ...@@ -144,6 +144,9 @@ public class LogSystem {
l.setFirstUncommittedPos(LOG_WRITTEN); l.setFirstUncommittedPos(LOG_WRITTEN);
} else if (l.getId() == firstUncommittedLog) { } else if (l.getId() == firstUncommittedLog) {
if (firstUncommittedPos == l.getPos()) { if (firstUncommittedPos == l.getPos()) {
// that means firstUncommittedPos is still
// were it was at the beginning
// and all sessions are committed
l.setFirstUncommittedPos(LOG_WRITTEN); l.setFirstUncommittedPos(LOG_WRITTEN);
} else { } else {
l.setFirstUncommittedPos(firstUncommittedPos); l.setFirstUncommittedPos(firstUncommittedPos);
......
...@@ -101,27 +101,27 @@ public abstract class TestHalt extends TestBase { ...@@ -101,27 +101,27 @@ public abstract class TestHalt extends TestBase {
/** /**
* Initialize the test. * Initialize the test.
*/ */
abstract void testInit() throws SQLException; abstract void controllerInit() throws SQLException;
/** /**
* Check if the database is consistent after a simulated database crash. * Check if the database is consistent after a simulated database crash.
*/ */
abstract void testCheckAfterCrash() throws SQLException; abstract void controllerCheckAfterCrash() throws SQLException;
/** /**
* Wait for some time after the application has been started. * Wait for some time after the application has been started.
*/ */
abstract void testWaitAfterAppStart() throws Exception; abstract void controllerWaitAfterAppStart() throws Exception;
/** /**
* Start the application. * Start the application.
*/ */
abstract void appStart() throws SQLException; abstract void processAppStart() throws SQLException;
/** /**
* Run the application. * Run the application.
*/ */
abstract void appRun() throws SQLException; abstract void processAppRun() throws SQLException;
public void test() throws SQLException { public void test() throws SQLException {
for (int i = 0;; i++) { for (int i = 0;; i++) {
...@@ -129,7 +129,7 @@ public abstract class TestHalt extends TestBase { ...@@ -129,7 +129,7 @@ public abstract class TestHalt extends TestBase {
flags = i >> 4; flags = i >> 4;
// flags |= FLAG_NO_DELAY; // | FLAG_LOBS; // flags |= FLAG_NO_DELAY; // | FLAG_LOBS;
try { try {
runTest(); controllerTest();
} catch (Throwable t) { } catch (Throwable t) {
System.out.println("Error: " + t); System.out.println("Error: " + t);
t.printStackTrace(); t.printStackTrace();
...@@ -139,37 +139,23 @@ public abstract class TestHalt extends TestBase { ...@@ -139,37 +139,23 @@ public abstract class TestHalt extends TestBase {
Connection getConnection() throws SQLException { Connection getConnection() throws SQLException {
org.h2.Driver.load(); org.h2.Driver.load();
return DriverManager.getConnection("jdbc:h2:" + baseDir + "/halt", "sa", "sa"); String url = "jdbc:h2:" + baseDir + "/halt";
// String url = "jdbc:h2:" + baseDir + "/halt;TRACE_LEVEL_FILE=3";
return DriverManager.getConnection(url, "sa", "sa");
} }
/** void processRunRandom() throws SQLException {
* Start the program.
*
* @param args the command line arguments
*/
protected void start(String[] args) throws Exception {
if (args.length == 0) {
runTest();
} else {
operations = Integer.parseInt(args[0]);
flags = Integer.parseInt(args[1]);
value = Integer.parseInt(args[2]);
runRandom();
}
}
private void runRandom() throws SQLException {
connect(); connect();
try { try {
traceOperation("connected, operations:" + operations + " flags:" + flags + " value:" + value); traceOperation("connected, operations:" + operations + " flags:" + flags + " value:" + value);
appStart(); processAppStart();
System.out.println("READY"); System.out.println("READY");
System.out.println("READY"); System.out.println("READY");
System.out.println("READY"); System.out.println("READY");
appRun(); processAppRun();
traceOperation("done"); traceOperation("done");
} catch (Exception e) { } catch (Exception e) {
trace("run", e); traceOperation("run", e);
} }
disconnect(); disconnect();
} }
...@@ -179,7 +165,7 @@ public abstract class TestHalt extends TestBase { ...@@ -179,7 +165,7 @@ public abstract class TestHalt extends TestBase {
traceOperation("connecting"); traceOperation("connecting");
conn = getConnection(); conn = getConnection();
} catch (SQLException e) { } catch (SQLException e) {
trace("connect", e); traceOperation("connect", e);
e.printStackTrace(); e.printStackTrace();
throw e; throw e;
} }
...@@ -191,7 +177,7 @@ public abstract class TestHalt extends TestBase { ...@@ -191,7 +177,7 @@ public abstract class TestHalt extends TestBase {
* @param s the message * @param s the message
*/ */
protected void traceOperation(String s) { protected void traceOperation(String s) {
trace(s, null); traceOperation(s, null);
} }
/** /**
...@@ -200,7 +186,7 @@ public abstract class TestHalt extends TestBase { ...@@ -200,7 +186,7 @@ public abstract class TestHalt extends TestBase {
* @param s the message * @param s the message
* @param e the exception or null * @param e the exception or null
*/ */
protected void trace(String s, Exception e) { protected void traceOperation(String s, Exception e) {
FileWriter writer = null; FileWriter writer = null;
try { try {
File f = new File(baseDir + "/" + TRACE_FILE_NAME); File f = new File(baseDir + "/" + TRACE_FILE_NAME);
...@@ -219,13 +205,13 @@ public abstract class TestHalt extends TestBase { ...@@ -219,13 +205,13 @@ public abstract class TestHalt extends TestBase {
} }
} }
private void runTest() throws Exception { void controllerTest() throws Exception {
traceOperation("delete database -----------------------------"); traceOperation("delete database -----------------------------");
DeleteDbFiles.execute(baseDir, DATABASE_NAME, true); DeleteDbFiles.execute(baseDir, DATABASE_NAME, true);
new File(baseDir + "/" + TRACE_FILE_NAME).delete(); new File(baseDir + "/" + TRACE_FILE_NAME).delete();
connect(); connect();
testInit(); controllerInit();
disconnect(); disconnect();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
traceOperation("backing up " + sequenceId); traceOperation("backing up " + sequenceId);
...@@ -254,7 +240,7 @@ public abstract class TestHalt extends TestBase { ...@@ -254,7 +240,7 @@ public abstract class TestHalt extends TestBase {
} else if (s.startsWith("READY")) { } else if (s.startsWith("READY")) {
traceOperation("got reply: " + s); traceOperation("got reply: " + s);
} }
testWaitAfterAppStart(); controllerWaitAfterAppStart();
p.destroy(); p.destroy();
try { try {
traceOperation("backing up " + sequenceId); traceOperation("backing up " + sequenceId);
...@@ -262,7 +248,7 @@ public abstract class TestHalt extends TestBase { ...@@ -262,7 +248,7 @@ public abstract class TestHalt extends TestBase {
// new File(BASE_DIR + "/haltSeq" + (sequenceId-20) + // new File(BASE_DIR + "/haltSeq" + (sequenceId-20) +
// ".zip").delete(); // ".zip").delete();
connect(); connect();
testCheckAfterCrash(); controllerCheckAfterCrash();
} catch (Exception e) { } catch (Exception e) {
File zip = new File(baseDir + "/haltSeq" + sequenceId + ".zip"); File zip = new File(baseDir + "/haltSeq" + sequenceId + ".zip");
File zipId = new File(baseDir + "/haltSeq" + sequenceId + "-" + errorId + ".zip"); File zipId = new File(baseDir + "/haltSeq" + sequenceId + "-" + errorId + ".zip");
...@@ -285,7 +271,7 @@ public abstract class TestHalt extends TestBase { ...@@ -285,7 +271,7 @@ public abstract class TestHalt extends TestBase {
traceOperation("disconnect"); traceOperation("disconnect");
conn.close(); conn.close();
} catch (Exception e) { } catch (Exception e) {
trace("disconnect", e); traceOperation("disconnect", e);
} }
} }
......
...@@ -29,7 +29,15 @@ public class TestHaltApp extends TestHalt { ...@@ -29,7 +29,15 @@ public class TestHaltApp extends TestHalt {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
SelfDestructor.startCountdown(60); SelfDestructor.startCountdown(60);
baseDir = TestHalt.DIR; baseDir = TestHalt.DIR;
new TestHaltApp().start(args); TestHaltApp app = new TestHaltApp();
if (args.length == 0) {
app.controllerTest();
} else {
app.operations = Integer.parseInt(args[0]);
app.flags = Integer.parseInt(args[1]);
app.value = Integer.parseInt(args[2]);
app.processRunRandom();
}
} }
private void execute(Statement stat, String sql) throws SQLException { private void execute(Statement stat, String sql) throws SQLException {
...@@ -40,7 +48,7 @@ public class TestHaltApp extends TestHalt { ...@@ -40,7 +48,7 @@ public class TestHaltApp extends TestHalt {
/** /**
* Initialize the database. * Initialize the database.
*/ */
protected void testInit() throws SQLException { protected void controllerInit() throws SQLException {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
// stat.execute("CREATE TABLE TEST(ID IDENTITY, NAME VARCHAR(255))"); // stat.execute("CREATE TABLE TEST(ID IDENTITY, NAME VARCHAR(255))");
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
...@@ -57,7 +65,7 @@ public class TestHaltApp extends TestHalt { ...@@ -57,7 +65,7 @@ public class TestHaltApp extends TestHalt {
/** /**
* Wait after the application has been started. * Wait after the application has been started.
*/ */
protected void testWaitAfterAppStart() throws Exception { protected void controllerWaitAfterAppStart() throws Exception {
int sleep = 10 + random.nextInt(300); int sleep = 10 + random.nextInt(300);
if ((flags & FLAG_NO_DELAY) == 0) { if ((flags & FLAG_NO_DELAY) == 0) {
sleep += 1000; sleep += 1000;
...@@ -71,22 +79,22 @@ public class TestHaltApp extends TestHalt { ...@@ -71,22 +79,22 @@ public class TestHaltApp extends TestHalt {
* *
* @throws SQLException if the data is not consistent. * @throws SQLException if the data is not consistent.
*/ */
protected void testCheckAfterCrash() throws SQLException { protected void controllerCheckAfterCrash() throws SQLException {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST"); ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST");
rs.next(); rs.next();
int count = rs.getInt(1); int count = rs.getInt(1);
System.out.println("count: " + count); System.out.println("count: " + count);
if (count % 2 == 0) { if (count % 2 != 0) {
traceOperation("row count: " + count); traceOperation("row count: " + count);
throw new SQLException("Unexpected odd row count"); throw new SQLException("Unexpected odd row count: " + count);
} }
} }
/** /**
* Initialize the application. * Initialize the application.
*/ */
protected void appStart() throws SQLException { protected void processAppStart() throws SQLException {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
if ((flags & FLAG_NO_DELAY) != 0) { if ((flags & FLAG_NO_DELAY) != 0) {
execute(stat, "SET WRITE_DELAY 0"); execute(stat, "SET WRITE_DELAY 0");
...@@ -95,13 +103,13 @@ public class TestHaltApp extends TestHalt { ...@@ -95,13 +103,13 @@ public class TestHaltApp extends TestHalt {
ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST"); ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST");
rs.next(); rs.next();
rowCount = rs.getInt(1); rowCount = rs.getInt(1);
trace("rows: " + rowCount, null); traceOperation("rows: " + rowCount, null);
} }
/** /**
* Run the application code. * Run the application code.
*/ */
protected void appRun() throws SQLException { protected void processAppRun() throws SQLException {
conn.setAutoCommit(false); conn.setAutoCommit(false);
traceOperation("setAutoCommit false"); traceOperation("setAutoCommit false");
int rows = 10000 + value; int rows = 10000 + value;
...@@ -146,11 +154,11 @@ public class TestHaltApp extends TestHalt { ...@@ -146,11 +154,11 @@ public class TestHaltApp extends TestHalt {
rowCount -= uc; rowCount -= uc;
} }
traceOperation("rowCount " + rowCount); traceOperation("rowCount " + rowCount);
trace("rows now: " + rowCount, null); traceOperation("rows now: " + rowCount, null);
if (rowCount % 2 == 0) { if (rowCount % 2 == 0) {
traceOperation("commit " + rowCount); traceOperation("commit " + rowCount);
conn.commit(); conn.commit();
trace("committed: " + rowCount, null); traceOperation("committed: " + rowCount, null);
} }
if ((flags & FLAG_NO_DELAY) != 0) { if ((flags & FLAG_NO_DELAY) != 0) {
if (random.nextInt(10) == 0 && (rowCount % 2 == 0)) { if (random.nextInt(10) == 0 && (rowCount % 2 == 0)) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论