提交 13ab7e1e authored 作者: andrei's avatar andrei

ensure Engine.close() is called

上级 cebf433e
...@@ -1256,110 +1256,112 @@ public class Database implements DataHandler { ...@@ -1256,110 +1256,112 @@ public class Database implements DataHandler {
* hook * hook
*/ */
void close(boolean fromShutdownHook) { void close(boolean fromShutdownHook) {
synchronized (this) { try {
if (closing) { synchronized (this) {
return; if (closing) {
}
throwLastBackgroundException();
if (fileLockMethod == FileLockMethod.SERIALIZED &&
!reconnectChangePending) {
// another connection may have written something - don't write
try {
closeOpenFilesAndUnlock(false);
} catch (DbException e) {
// ignore
}
traceSystem.close();
Engine.getInstance().close(databaseName);
return;
}
closing = true;
stopServer();
if (!userSessions.isEmpty()) {
if (!fromShutdownHook) {
return; return;
} }
trace.info("closing {0} from shutdown hook", databaseName); throwLastBackgroundException();
closeAllSessionsException(null); if (fileLockMethod == FileLockMethod.SERIALIZED &&
} !reconnectChangePending) {
trace.info("closing {0}", databaseName); // another connection may have written something - don't write
if (eventListener != null) { try {
// allow the event listener to connect to the database closeOpenFilesAndUnlock(false);
closing = false; } catch (DbException e) {
DatabaseEventListener e = eventListener; // ignore
// set it to null, to make sure it's called only once }
eventListener = null; traceSystem.close();
e.closingDatabase();
if (!userSessions.isEmpty()) {
// if a connection was opened, we can't close the database
return; return;
} }
closing = true; closing = true;
stopServer();
if (!userSessions.isEmpty()) {
if (!fromShutdownHook) {
return;
}
trace.info("closing {0} from shutdown hook", databaseName);
closeAllSessionsException(null);
}
trace.info("closing {0}", databaseName);
if (eventListener != null) {
// allow the event listener to connect to the database
closing = false;
DatabaseEventListener e = eventListener;
// set it to null, to make sure it's called only once
eventListener = null;
e.closingDatabase();
if (!userSessions.isEmpty()) {
// if a connection was opened, we can't close the database
return;
}
closing = true;
}
} }
} removeOrphanedLobs();
removeOrphanedLobs(); try {
try { if (systemSession != null) {
if (systemSession != null) { if (powerOffCount != -1) {
if (powerOffCount != -1) { for (Table table : getAllTablesAndViews(false)) {
for (Table table : getAllTablesAndViews(false)) { if (table.isGlobalTemporary()) {
if (table.isGlobalTemporary()) { table.removeChildrenAndResources(systemSession);
table.removeChildrenAndResources(systemSession); } else {
} else { table.close(systemSession);
table.close(systemSession); }
}
for (SchemaObject obj : getAllSchemaObjects(
DbObject.SEQUENCE)) {
Sequence sequence = (Sequence) obj;
sequence.close();
} }
} }
for (SchemaObject obj : getAllSchemaObjects( for (SchemaObject obj : getAllSchemaObjects(
DbObject.SEQUENCE)) { DbObject.TRIGGER)) {
Sequence sequence = (Sequence) obj; TriggerObject trigger = (TriggerObject) obj;
sequence.close(); try {
trigger.close();
} catch (SQLException e) {
trace.error(e, "close");
}
} }
} if (powerOffCount != -1) {
for (SchemaObject obj : getAllSchemaObjects( meta.close(systemSession);
DbObject.TRIGGER)) { systemSession.commit(true);
TriggerObject trigger = (TriggerObject) obj;
try {
trigger.close();
} catch (SQLException e) {
trace.error(e, "close");
} }
} }
if (powerOffCount != -1) { } catch (DbException e) {
meta.close(systemSession); trace.error(e, "close");
systemSession.commit(true);
}
} }
} catch (DbException e) { tempFileDeleter.deleteAll();
trace.error(e, "close");
}
tempFileDeleter.deleteAll();
try {
closeOpenFilesAndUnlock(true);
} catch (DbException e) {
trace.error(e, "close");
}
trace.info("closed");
traceSystem.close();
if (closeOnExit != null) {
closeOnExit.reset();
try { try {
Runtime.getRuntime().removeShutdownHook(closeOnExit); closeOpenFilesAndUnlock(true);
} catch (IllegalStateException e) { } catch (DbException e) {
// ignore trace.error(e, "close");
} catch (SecurityException e) {
// applets may not do that - ignore
} }
closeOnExit = null; trace.info("closed");
} traceSystem.close();
Engine.getInstance().close(databaseName); if (closeOnExit != null) {
if (deleteFilesOnDisconnect && persistent) { closeOnExit.reset();
deleteFilesOnDisconnect = false; try {
try { Runtime.getRuntime().removeShutdownHook(closeOnExit);
String directory = FileUtils.getParent(databaseName); } catch (IllegalStateException e) {
String name = FileUtils.getName(databaseName); // ignore
DeleteDbFiles.execute(directory, name, true); } catch (SecurityException e) {
} catch (Exception e) { // applets may not do that - ignore
// ignore (the trace is closed already) }
closeOnExit = null;
}
if (deleteFilesOnDisconnect && persistent) {
deleteFilesOnDisconnect = false;
try {
String directory = FileUtils.getParent(databaseName);
String name = FileUtils.getName(databaseName);
DeleteDbFiles.execute(directory, name, true);
} catch (Exception e) {
// ignore (the trace is closed already)
}
} }
} finally {
Engine.getInstance().close(databaseName);
} }
} }
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
*/ */
package org.h2.engine; package org.h2.engine;
import java.util.Hashtable; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
...@@ -28,7 +28,7 @@ import org.h2.util.Utils; ...@@ -28,7 +28,7 @@ import org.h2.util.Utils;
public class Engine implements SessionFactory { public class Engine implements SessionFactory {
private static final Engine INSTANCE = new Engine(); private static final Engine INSTANCE = new Engine();
private static final Map<String, Database> DATABASES = new Hashtable<>(); private static final Map<String, Database> DATABASES = new HashMap<>();
private volatile long wrongPasswordDelay = private volatile long wrongPasswordDelay =
SysProperties.DELAY_WRONG_PASSWORD_MIN; SysProperties.DELAY_WRONG_PASSWORD_MIN;
...@@ -51,30 +51,32 @@ public class Engine implements SessionFactory { ...@@ -51,30 +51,32 @@ public class Engine implements SessionFactory {
Database database; Database database;
ci.removeProperty("NO_UPGRADE", false); ci.removeProperty("NO_UPGRADE", false);
boolean openNew = ci.getProperty("OPEN_NEW", false); boolean openNew = ci.getProperty("OPEN_NEW", false);
if (openNew || ci.isUnnamedInMemory()) {
database = null;
} else {
database = DATABASES.get(name);
}
User user = null;
boolean opened = false; boolean opened = false;
if (database == null) { User user = null;
if (ifExists && !Database.exists(name)) { synchronized (DATABASES) {
throw DbException.get(ErrorCode.DATABASE_NOT_FOUND_1, name); if (openNew || ci.isUnnamedInMemory()) {
} database = null;
database = new Database(ci, cipher); } else {
opened = true; database = DATABASES.get(name);
if (database.getAllUsers().isEmpty()) {
// users is the last thing we add, so if no user is around,
// the database is new (or not initialized correctly)
user = new User(database, database.allocateObjectId(),
ci.getUserName(), false);
user.setAdmin(true);
user.setUserPasswordHash(ci.getUserPasswordHash());
database.setMasterUser(user);
} }
if (!ci.isUnnamedInMemory()) { if (database == null) {
DATABASES.put(name, database); if (ifExists && !Database.exists(name)) {
throw DbException.get(ErrorCode.DATABASE_NOT_FOUND_1, name);
}
database = new Database(ci, cipher);
opened = true;
if (database.getAllUsers().isEmpty()) {
// users is the last thing we add, so if no user is around,
// the database is new (or not initialized correctly)
user = new User(database, database.allocateObjectId(),
ci.getUserName(), false);
user.setAdmin(true);
user.setUserPasswordHash(ci.getUserPasswordHash());
database.setMasterUser(user);
}
if (!ci.isUnnamedInMemory()) {
DATABASES.put(name, database);
}
} }
} }
if (opened) { if (opened) {
...@@ -273,7 +275,9 @@ public class Engine implements SessionFactory { ...@@ -273,7 +275,9 @@ public class Engine implements SessionFactory {
throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1, e, "JMX"); throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1, e, "JMX");
} }
} }
DATABASES.remove(name); synchronized (DATABASES) {
DATABASES.remove(name);
}
} }
/** /**
......
...@@ -118,10 +118,7 @@ public class TestOutOfMemory extends TestBase { ...@@ -118,10 +118,7 @@ public class TestOutOfMemory extends TestBase {
ErrorCode.DATABASE_IS_CLOSED == e.getErrorCode() || ErrorCode.DATABASE_IS_CLOSED == e.getErrorCode() ||
ErrorCode.GENERAL_ERROR_1 == e.getErrorCode()); ErrorCode.GENERAL_ERROR_1 == e.getErrorCode());
} }
for (int i = 0; i < 5; i++) { recoverAfterOOM();
System.gc();
Thread.sleep(20);
}
try { try {
conn.close(); conn.close();
fail(); fail();
...@@ -132,10 +129,7 @@ public class TestOutOfMemory extends TestBase { ...@@ -132,10 +129,7 @@ public class TestOutOfMemory extends TestBase {
ErrorCode.DATABASE_IS_CLOSED == e.getErrorCode() || ErrorCode.DATABASE_IS_CLOSED == e.getErrorCode() ||
ErrorCode.GENERAL_ERROR_1 == e.getErrorCode()); ErrorCode.GENERAL_ERROR_1 == e.getErrorCode());
} }
for (int i = 0; i < 5; i++) { recoverAfterOOM();
System.gc();
Thread.sleep(20);
}
conn = DriverManager.getConnection(url); conn = DriverManager.getConnection(url);
stat = conn.createStatement(); stat = conn.createStatement();
stat.execute("SELECT 1"); stat.execute("SELECT 1");
...@@ -146,14 +140,18 @@ public class TestOutOfMemory extends TestBase { ...@@ -146,14 +140,18 @@ public class TestOutOfMemory extends TestBase {
} }
} }
private void testUpdateWhenNearlyOutOfMemory() throws SQLException, InterruptedException { private static void recoverAfterOOM() throws InterruptedException {
if (config.memory) {
return;
}
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
System.gc(); System.gc();
Thread.sleep(20); Thread.sleep(20);
} }
}
private void testUpdateWhenNearlyOutOfMemory() throws SQLException, InterruptedException {
if (config.memory) {
return;
}
recoverAfterOOM();
deleteDb("outOfMemory"); deleteDb("outOfMemory");
Connection conn = getConnection("outOfMemory;MAX_OPERATION_MEMORY=1000000"); Connection conn = getConnection("outOfMemory;MAX_OPERATION_MEMORY=1000000");
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
...@@ -171,11 +169,12 @@ public class TestOutOfMemory extends TestBase { ...@@ -171,11 +169,12 @@ public class TestOutOfMemory extends TestBase {
fail(); fail();
} catch(DbException ex) { } catch(DbException ex) {
freeMemory(); freeMemory();
assertEquals(ErrorCode.OUT_OF_MEMORY, ex.getErrorCode()); assertTrue(ErrorCode.OUT_OF_MEMORY == ex.getErrorCode() || ErrorCode.GENERAL_ERROR_1 == ex.getErrorCode());
} catch (SQLException ex) { } catch (SQLException ex) {
freeMemory(); freeMemory();
assertEquals(ErrorCode.OUT_OF_MEMORY, ex.getErrorCode()); assertTrue(ErrorCode.OUT_OF_MEMORY == ex.getErrorCode() || ErrorCode.GENERAL_ERROR_1 == ex.getErrorCode());
} }
recoverAfterOOM();
try { try {
conn.close(); conn.close();
fail(); fail();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论