提交 24216566 authored 作者: Thomas Mueller's avatar Thomas Mueller

If the transaction log could not be truncated because of an uncommitted…

If the transaction log could not be truncated because of an uncommitted transaction, now "Transaction log could not be truncated" is written to the .trace.db file.
上级 50672dca
......@@ -18,7 +18,13 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>ALTER TABLE ADD can now add more than one column at a time.
<ul><li>If the transaction log could not be truncated because of an uncommitted transaction,
now "Transaction log could not be truncated" is written to the .trace.db file.
Before, the database file was growing and it was hard to find out what the root cause was.
To avoid the database file from growing, a new feature to automatically rollback the oldest
transaction is available now. To enable it, append ;LOG_SIZE_LIMIT=32 to the database URL
(in that case, the oldest session is rolled back if the transaction log is 32 MB).
</li><li>ALTER TABLE ADD can now add more than one column at a time.
</li><li>Issue 380: ALTER TABLE ADD FOREIGN KEY with an explicit index didn't verify
the index can be used, which would lead to a NullPointerException later on.
</li><li>The wrong kind of exception (NullPointerException) was thrown in a UNION query
......
......@@ -311,6 +311,15 @@ public class DbSettings extends SettingsBase {
*/
public final boolean shareLinkedConnections = get("SHARE_LINKED_CONNECTIONS", true);
/**
* Database setting <code>LOG_SIZE_LIMIT</code>
* (default: 0).<br />
* The maximum size of the transaction log (in MB). If the transaction log is bigger
* than this value the oldest session is rolled back. The value 0 means the
* sessions are never rolled back.
*/
public long logSizeLimit = get("LOG_SIZE_LIMIT", 0);
private DbSettings(HashMap<String, String> s) {
super(s);
}
......
......@@ -141,6 +141,7 @@ public class PageStore implements CacheWriter {
private Cache cache;
private int freeListPagesPerList;
private boolean recoveryRunning;
private boolean ignoreBigLog;
/**
* The index to the first free-list page that potentially has free space.
......@@ -1427,9 +1428,38 @@ public class PageStore implements CacheWriter {
checkOpen();
openForWriting();
log.commit(session.getId());
if (log.getSize() - logSizeBase > maxLogSize) {
long size = log.getSize();
if (size - logSizeBase > maxLogSize) {
checkpoint();
logSizeBase = log.getSize();
if (ignoreBigLog) {
return;
}
long newSize = log.getSize();
if (newSize < size || size < maxLogSize) {
ignoreBigLog = false;
return;
}
ignoreBigLog = true;
trace.error(null, "Transaction log could not be truncated; size: " + (newSize / 1024 / 1024) + " MB");
long logSizeLimit = database.getSettings().logSizeLimit;
if (logSizeLimit == 0 || newSize < logSizeLimit) {
return;
}
int firstUncommittedSection = log.getLogSectionId();
Session[] sessions = database.getSessions(true);
Session oldestSession = null;
for (Session s : sessions) {
int firstUncommitted = s.getFirstUncommittedLog();
if (firstUncommitted != Session.LOG_WRITTEN) {
if (firstUncommitted < firstUncommittedSection) {
firstUncommittedSection = firstUncommitted;
oldestSession = s;
}
}
}
trace.info("Rolling back session #" +oldestSession.getId() + " (the oldest uncommitted)");
oldestSession.rollback();
logSizeBase = newSize;
}
}
......
......@@ -6,6 +6,8 @@
*/
package org.h2.test.unit;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
......@@ -22,6 +24,7 @@ import org.h2.result.Row;
import org.h2.store.Page;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
import org.h2.util.IOUtils;
import org.h2.util.New;
/**
......@@ -43,6 +46,7 @@ public class TestPageStore extends TestBase implements DatabaseEventListener {
}
public void test() throws Exception {
testLogLimit();
testRecoverLobInDatabase();
testWriteTransactionLogBeforeData();
testDefrag();
......@@ -73,6 +77,52 @@ public class TestPageStore extends TestBase implements DatabaseEventListener {
deleteDb("pageStore");
}
private void testLogLimit() throws Exception {
testLogLimit(false);
testLogLimit(true);
}
private void testLogLimit(boolean autoRollback) throws Exception {
deleteDb("pageStore");
Connection conn, conn2;
Statement stat, stat2;
String url = "pageStore;TRACE_LEVEL_FILE=2";
if (autoRollback) {
url += ";LOG_SIZE_LIMIT=1";
}
conn = getConnection(url);
stat = conn.createStatement();
stat.execute("create table test(id int primary key)");
conn.setAutoCommit(false);
stat.execute("insert into test values(1)");
conn2 = getConnection(url);
stat2 = conn2.createStatement();
stat2.execute("create table t2(id identity, name varchar)");
stat2.execute("set max_log_size 1");
for (int i = 0; i < 10; i++) {
stat2.execute("insert into t2(name) select space(100) from system_range(1, 1000)");
}
InputStream in = FileUtils.newInputStream(getBaseDir() + "/pageStore.trace.db");
String s = IOUtils.readStringAndClose(new InputStreamReader(in), -1);
assertTrue(s.indexOf("Transaction log could not be truncated") > 0);
if (autoRollback) {
assertTrue(s, s.indexOf("Rolling back session") > 0);
} else {
assertTrue(s, s.indexOf("Rolling back session") < 0);
}
conn.commit();
ResultSet rs = stat2.executeQuery("select * from test");
if (autoRollback) {
assertFalse(rs.next());
} else {
assertTrue(rs.next());
}
conn2.close();
conn.close();
}
private void testRecoverLobInDatabase() throws SQLException {
deleteDb("pageStore");
String url = getURL("pageStore;MVCC=TRUE;CACHE_SIZE=1", true);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论