提交 b413c56a authored 作者: andrei's avatar andrei

Propagate exception from backgound thread

Make OOME test a forked process
上级 bbbd555d
...@@ -2450,27 +2450,27 @@ public final class MVStore { ...@@ -2450,27 +2450,27 @@ public final class MVStore {
* needed. * needed.
*/ */
void writeInBackground() { void writeInBackground() {
if (closed) { try {
return; if (closed) {
} return;
}
// could also commit when there are many unsaved pages, // could also commit 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
long time = getTimeSinceCreation(); long time = getTimeSinceCreation();
if (time <= lastCommitTime + autoCommitDelay) { if (time <= lastCommitTime + autoCommitDelay) {
return;
}
if (hasUnsavedChanges()) {
try {
commitAndSave();
} catch (Throwable e) {
handleException(e);
return; return;
} }
} if (hasUnsavedChanges()) {
if (autoCompactFillRate > 0) { try {
try { commitAndSave();
} catch (Throwable e) {
handleException(e);
return;
}
}
if (autoCompactFillRate > 0) {
// whether there were file read or write operations since // whether there were file read or write operations since
// the last time // the last time
boolean fileOps; boolean fileOps;
...@@ -2486,9 +2486,9 @@ public final class MVStore { ...@@ -2486,9 +2486,9 @@ public final class MVStore {
// in the bookkeeping? // in the bookkeeping?
compact(fillRate, autoCommitMemory); compact(fillRate, autoCommitMemory);
autoCompactLastFileOpCount = fileStore.getWriteCount() + fileStore.getReadCount(); autoCompactLastFileOpCount = fileStore.getWriteCount() + fileStore.getReadCount();
} catch (Throwable e) {
handleException(e);
} }
} catch (Throwable e) {
handleException(e);
} }
} }
...@@ -2497,7 +2497,9 @@ public final class MVStore { ...@@ -2497,7 +2497,9 @@ public final class MVStore {
try { try {
backgroundExceptionHandler.uncaughtException(null, ex); backgroundExceptionHandler.uncaughtException(null, ex);
} catch(Throwable ignore) { } catch(Throwable ignore) {
ex.addSuppressed(ignore); if (ex != ignore) { // OOME may be the same
ex.addSuppressed(ignore);
}
} }
} }
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
package org.h2.test.db; package org.h2.test.db;
import java.io.File;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
...@@ -15,12 +16,12 @@ import java.util.Map; ...@@ -15,12 +16,12 @@ import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.mvstore.MVStore; import org.h2.mvstore.MVStore;
import org.h2.store.fs.FilePath; import org.h2.store.fs.FilePath;
import org.h2.store.fs.FilePathMem; import org.h2.store.fs.FilePathMem;
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.SelfDestructor;
/** /**
* Tests out of memory situations. The database must not get corrupted, and * Tests out of memory situations. The database must not get corrupted, and
...@@ -38,7 +39,7 @@ public class TestOutOfMemory extends TestBase { ...@@ -38,7 +39,7 @@ public class TestOutOfMemory extends TestBase {
} }
@Override @Override
public void test() throws SQLException, InterruptedException { public void test() throws Exception {
if (config.vmlens) { if (config.vmlens) {
// running out of memory will cause the vmlens agent to stop working // running out of memory will cause the vmlens agent to stop working
return; return;
...@@ -147,67 +148,73 @@ public class TestOutOfMemory extends TestBase { ...@@ -147,67 +148,73 @@ public class TestOutOfMemory extends TestBase {
} }
} }
private void testUpdateWhenNearlyOutOfMemory() throws SQLException, InterruptedException { private void testUpdateWhenNearlyOutOfMemory() throws Exception {
if (config.memory) { if (config.memory) {
return; return;
} }
recoverAfterOOM();
deleteDb("outOfMemory"); deleteDb("outOfMemory");
Connection conn = getConnection("outOfMemory;MAX_OPERATION_MEMORY=1000000"); String url = getURL("outOfMemory", true);
Statement stat = conn.createStatement(); //*
stat.execute("drop all objects"); String selfDestructor = SelfDestructor.getPropertyString(1);
stat.execute("create table stuff (id int, text varchar as space(100) || id)"); String args[] = {System.getProperty("java.home") + File.separatorChar + "bin" + File.separator + "java",
stat.execute("insert into stuff(id) select x from system_range(1, 3000)"); "-Xmx128m", "-XX:+UseParallelGC", // "-XX:+UseG1GC",
PreparedStatement prep = conn.prepareStatement( "-cp", getClassPath(),
"update stuff set text = text || space(1000) || id"); selfDestructor,
prep.execute(); Child.class.getName(), url};
stat.execute("checkpoint"); ProcessBuilder pb = new ProcessBuilder()
eatMemory(80); .redirectOutput(ProcessBuilder.Redirect.INHERIT)
try { .redirectError(ProcessBuilder.Redirect.INHERIT)
.command(args);
Process p = pb.start();
int exitValue = p.waitFor();
assertEquals("Exit code", 0, exitValue);
/*/
Child.main(url);
//*/
try (Connection conn = getConnection("outOfMemory")) {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT count(*) FROM stuff");
assertTrue(rs.next());
assertEquals(3000, rs.getInt(1));
rs = stat.executeQuery("SELECT * FROM stuff WHERE id = 3000");
assertTrue(rs.next());
String text = rs.getString(2);
assertFalse(rs.wasNull());
assertEquals(1004, text.length());
} finally {
deleteDb("outOfMemory");
}
}
public static final class Child extends TestBase
{
private static String URL;
public static void main(String... a) throws Exception {
URL = a[0];
TestBase.createCaller().init().test();
}
@Override
public void test() throws SQLException {
Connection conn = getConnection(URL + ";MAX_OPERATION_MEMORY=1000000");
Statement stat = conn.createStatement();
stat.execute("DROP ALL OBJECTS");
stat.execute("CREATE TABLE stuff (id INT, text VARCHAR)");
stat.execute("INSERT INTO stuff(id) SELECT x FROM system_range(1, 3000)");
PreparedStatement prep = conn.prepareStatement(
"UPDATE stuff SET text = IFNULL(text,'') || space(1000) || id");
prep.execute();
stat.execute("CHECKPOINT");
eatMemory(80);
try { try {
prep.execute(); prep.execute();
fail(); fail();
} catch(DbException ex) { } catch (Throwable ex) {
freeMemory();
assertTrue(ErrorCode.OUT_OF_MEMORY == ex.getErrorCode() || ErrorCode.GENERAL_ERROR_1 == ex.getErrorCode());
} catch (SQLException ex) {
freeMemory();
assertTrue(ErrorCode.OUT_OF_MEMORY == ex.getErrorCode() || ErrorCode.GENERAL_ERROR_1 == ex.getErrorCode());
}
recoverAfterOOM();
try {
conn.close();
fail();
} catch(DbException ex) {
freeMemory();
assertEquals(ErrorCode.DATABASE_IS_CLOSED, ex.getErrorCode());
} catch (SQLException ex) {
freeMemory(); freeMemory();
assertEquals(ErrorCode.DATABASE_IS_CLOSED, ex.getErrorCode()); } finally {
} try { conn.close(); } catch (Throwable ignore) {/**/}
freeMemory();
conn = null;
conn = getConnection("outOfMemory");
stat = conn.createStatement();
ResultSet rs = stat.executeQuery("select count(*) from stuff");
rs.next();
assertEquals(3000, rs.getInt(1));
} catch (OutOfMemoryError e) {
freeMemory();
// out of memory not detected
throw new AssertionError("Out of memory not detected", e);
} finally {
freeMemory();
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// out of memory will / may close the database
assertKnownException(e);
}
} }
} }
deleteDb("outOfMemory");
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论