Unverified 提交 7114e5d4 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1229 from katzyn/undolog

Create UndoLog only when necessary and remove outdated code
...@@ -290,7 +290,7 @@ public abstract class Command implements CommandInterface { ...@@ -290,7 +290,7 @@ public abstract class Command implements CommandInterface {
if (s.getErrorCode() == ErrorCode.DEADLOCK_1) { if (s.getErrorCode() == ErrorCode.DEADLOCK_1) {
session.rollback(); session.rollback();
} else { } else {
session.rollbackTo(rollback, false); session.rollbackTo(rollback);
} }
} catch (Throwable nested) { } catch (Throwable nested) {
e.addSuppressed(nested); e.addSuppressed(nested);
......
...@@ -130,12 +130,6 @@ public class DbSettings extends SettingsBase { ...@@ -130,12 +130,6 @@ public class DbSettings extends SettingsBase {
*/ */
public final boolean functionsInSchema = get("FUNCTIONS_IN_SCHEMA", true); public final boolean functionsInSchema = get("FUNCTIONS_IN_SCHEMA", true);
/**
* Database setting <code>LARGE_TRANSACTIONS</code> (default: true).<br />
* Support very large transactions
*/
public final boolean largeTransactions = get("LARGE_TRANSACTIONS", true);
/** /**
* Database setting <code>LOB_TIMEOUT</code> (default: 300000, * Database setting <code>LOB_TIMEOUT</code> (default: 300000,
* which means 5 minutes).<br /> * which means 5 minutes).<br />
......
...@@ -85,7 +85,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -85,7 +85,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
private final User user; private final User user;
private final int id; private final int id;
private final ArrayList<Table> locks = Utils.newSmallArrayList(); private final ArrayList<Table> locks = Utils.newSmallArrayList();
private final UndoLog undoLog; private UndoLog undoLog;
private boolean autoCommit = true; private boolean autoCommit = true;
private Random random; private Random random;
private int lockTimeout; private int lockTimeout;
...@@ -168,7 +168,6 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -168,7 +168,6 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
this.database = database; this.database = database;
this.queryTimeout = database.getSettings().maxQueryTimeout; this.queryTimeout = database.getSettings().maxQueryTimeout;
this.queryCacheSize = database.getSettings().queryCacheSize; this.queryCacheSize = database.getSettings().queryCacheSize;
this.undoLog = new UndoLog(this);
this.user = user; this.user = user;
this.id = id; this.id = id;
this.lockTimeout = database.getLockTimeout(); this.lockTimeout = database.getLockTimeout();
...@@ -691,7 +690,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -691,7 +690,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
database.commit(this); database.commit(this);
} }
removeTemporaryLobs(true); removeTemporaryLobs(true);
if (undoLog.size() > 0) { if (undoLog != null && undoLog.size() > 0) {
undoLog.clear(); undoLog.clear();
} }
if (!ddl) { if (!ddl) {
...@@ -761,9 +760,9 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -761,9 +760,9 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
checkCommitRollback(); checkCommitRollback();
currentTransactionName = null; currentTransactionName = null;
transactionStart = null; transactionStart = null;
boolean needCommit = undoLog.size() > 0 || transaction != null; boolean needCommit = undoLog != null && undoLog.size() > 0 || transaction != null;
if(needCommit) { if (needCommit) {
rollbackTo(null, false); rollbackTo(null);
} }
if (!locks.isEmpty() || needCommit) { if (!locks.isEmpty() || needCommit) {
database.commit(this); database.commit(this);
...@@ -780,14 +779,15 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -780,14 +779,15 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
* Partially roll back the current transaction. * Partially roll back the current transaction.
* *
* @param savepoint the savepoint to which should be rolled back * @param savepoint the savepoint to which should be rolled back
* @param trimToSize if the list should be trimmed
*/ */
public void rollbackTo(Savepoint savepoint, boolean trimToSize) { public void rollbackTo(Savepoint savepoint) {
int index = savepoint == null ? 0 : savepoint.logIndex; int index = savepoint == null ? 0 : savepoint.logIndex;
if (undoLog != null) {
while (undoLog.size() > index) { while (undoLog.size() > index) {
UndoLogRecord entry = undoLog.getLast(); UndoLogRecord entry = undoLog.getLast();
entry.undo(this); entry.undo(this);
undoLog.removeLast(trimToSize); undoLog.removeLast();
}
} }
if (transaction != null) { if (transaction != null) {
if (savepoint == null) { if (savepoint == null) {
...@@ -818,7 +818,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -818,7 +818,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
@Override @Override
public boolean hasPendingTransaction() { public boolean hasPendingTransaction() {
return undoLog.size() > 0; return undoLog != null && undoLog.size() > 0;
} }
/** /**
...@@ -828,7 +828,9 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -828,7 +828,9 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
*/ */
public Savepoint setSavepoint() { public Savepoint setSavepoint() {
Savepoint sp = new Savepoint(); Savepoint sp = new Savepoint();
if (undoLog != null) {
sp.logIndex = undoLog.size(); sp.logIndex = undoLog.size();
}
if (database.getMvStore() != null) { if (database.getMvStore() != null) {
sp.transactionSavepoint = getStatementSavepoint(); sp.transactionSavepoint = getStatementSavepoint();
} }
...@@ -856,7 +858,9 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -856,7 +858,9 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
removeTemporaryLobs(false); removeTemporaryLobs(false);
cleanTempTables(true); cleanTempTables(true);
if (undoLog != null) {
undoLog.clear(); undoLog.clear();
}
// Table#removeChildrenAndResources can take the meta lock, // Table#removeChildrenAndResources can take the meta lock,
// and we need to unlock before we call removeSession(), which might // and we need to unlock before we call removeSession(), which might
// want to take the meta lock using the system session. // want to take the meta lock using the system session.
...@@ -910,6 +914,9 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -910,6 +914,9 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
} }
} }
} }
if (undoLog == null) {
undoLog = new UndoLog(database);
}
undoLog.add(log); undoLog.add(log);
} }
} }
...@@ -942,7 +949,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -942,7 +949,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
private void unlockAll() { private void unlockAll() {
if (SysProperties.CHECK) { if (SysProperties.CHECK) {
if (undoLog.size() > 0) { if (undoLog != null && undoLog.size() > 0) {
DbException.throwInternalError(); DbException.throwInternalError();
} }
} }
...@@ -1085,12 +1092,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -1085,12 +1092,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
if (savepoints == null) { if (savepoints == null) {
savepoints = database.newStringMap(); savepoints = database.newStringMap();
} }
Savepoint sp = new Savepoint(); savepoints.put(name, setSavepoint());
sp.logIndex = undoLog.size();
if (database.getMvStore() != null) {
sp.transactionSavepoint = getStatementSavepoint();
}
savepoints.put(name, sp);
} }
/** /**
...@@ -1109,7 +1111,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -1109,7 +1111,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
if (savepoint == null) { if (savepoint == null) {
throw DbException.get(ErrorCode.SAVEPOINT_IS_INVALID_1, name); throw DbException.get(ErrorCode.SAVEPOINT_IS_INVALID_1, name);
} }
rollbackTo(savepoint, false); rollbackTo(savepoint);
} }
/** /**
...@@ -1619,7 +1621,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -1619,7 +1621,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
if (!database.isPersistent()) { if (!database.isPersistent()) {
return ValueNull.INSTANCE; return ValueNull.INSTANCE;
} }
if (undoLog.size() == 0) { if (undoLog == null || undoLog.size() == 0) {
return ValueNull.INSTANCE; return ValueNull.INSTANCE;
} }
return ValueString.get(firstUncommittedLog + "-" + firstUncommittedPos + return ValueString.get(firstUncommittedLog + "-" + firstUncommittedPos +
......
...@@ -8,7 +8,6 @@ package org.h2.engine; ...@@ -8,7 +8,6 @@ package org.h2.engine;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import org.h2.message.DbException;
import org.h2.store.Data; import org.h2.store.Data;
import org.h2.store.FileStore; import org.h2.store.FileStore;
import org.h2.table.Table; import org.h2.table.Table;
...@@ -27,16 +26,14 @@ public class UndoLog { ...@@ -27,16 +26,14 @@ public class UndoLog {
private int memoryUndo; private int memoryUndo;
private int storedEntries; private int storedEntries;
private HashMap<Integer, Table> tables; private HashMap<Integer, Table> tables;
private final boolean largeTransactions;
/** /**
* Create a new undo log for the given session. * Create a new undo log for the given session.
* *
* @param session the session * @param database the database
*/ */
UndoLog(Session session) { UndoLog(Database database) {
this.database = session.getDatabase(); this.database = database;
largeTransactions = database.getSettings().largeTransactions;
} }
/** /**
...@@ -45,14 +42,8 @@ public class UndoLog { ...@@ -45,14 +42,8 @@ public class UndoLog {
* @return the number of rows * @return the number of rows
*/ */
int size() { int size() {
if (largeTransactions) {
return storedEntries + records.size(); return storedEntries + records.size();
} }
if (SysProperties.CHECK && memoryUndo > records.size()) {
DbException.throwInternalError();
}
return records.size();
}
/** /**
* Clear the undo log. This method is called after the transaction is * Clear the undo log. This method is called after the transaction is
...@@ -77,7 +68,6 @@ public class UndoLog { ...@@ -77,7 +68,6 @@ public class UndoLog {
*/ */
public UndoLogRecord getLast() { public UndoLogRecord getLast() {
int i = records.size() - 1; int i = records.size() - 1;
if (largeTransactions) {
if (i < 0 && storedEntries > 0) { if (i < 0 && storedEntries > 0) {
int last = storedEntriesPos.size() - 1; int last = storedEntriesPos.size() - 1;
long pos = storedEntriesPos.remove(last); long pos = storedEntriesPos.remove(last);
...@@ -96,7 +86,6 @@ public class UndoLog { ...@@ -96,7 +86,6 @@ public class UndoLog {
file.seek(pos); file.seek(pos);
} }
i = records.size() - 1; i = records.size() - 1;
}
UndoLogRecord entry = records.get(i); UndoLogRecord entry = records.get(i);
if (entry.isStored()) { if (entry.isStored()) {
int start = Math.max(0, i - database.getMaxMemoryUndo() / 2); int start = Math.max(0, i - database.getMaxMemoryUndo() / 2);
...@@ -131,18 +120,13 @@ public class UndoLog { ...@@ -131,18 +120,13 @@ public class UndoLog {
/** /**
* Remove the last record from the list of operations. * Remove the last record from the list of operations.
*
* @param trimToSize if the undo array should shrink to conserve memory
*/ */
void removeLast(boolean trimToSize) { void removeLast() {
int i = records.size() - 1; int i = records.size() - 1;
UndoLogRecord r = records.remove(i); UndoLogRecord r = records.remove(i);
if (!r.isStored()) { if (!r.isStored()) {
memoryUndo--; memoryUndo--;
} }
if (trimToSize && i > 1024 && (i & 1023) == 0) {
records.trimToSize();
}
} }
/** /**
...@@ -152,7 +136,6 @@ public class UndoLog { ...@@ -152,7 +136,6 @@ public class UndoLog {
*/ */
void add(UndoLogRecord entry) { void add(UndoLogRecord entry) {
records.add(entry); records.add(entry);
if (largeTransactions) {
memoryUndo++; memoryUndo++;
if (memoryUndo > database.getMaxMemoryUndo() && if (memoryUndo > database.getMaxMemoryUndo() &&
database.isPersistent() && database.isPersistent() &&
...@@ -179,36 +162,6 @@ public class UndoLog { ...@@ -179,36 +162,6 @@ public class UndoLog {
records.clear(); records.clear();
file.autoDelete(); file.autoDelete();
} }
} else {
if (!entry.isStored()) {
memoryUndo++;
}
if (memoryUndo > database.getMaxMemoryUndo() &&
database.isPersistent() &&
!database.isMVStore()) {
if (file == null) {
String fileName = database.createTempFile();
file = database.openFile(fileName, "rw", false);
file.setCheckedWriting(false);
file.seek(FileStore.HEADER_LENGTH);
rowBuff = Data.create(database, Constants.DEFAULT_PAGE_SIZE);
Data buff = rowBuff;
for (UndoLogRecord r : records) {
saveIfPossible(r, buff);
}
} else {
saveIfPossible(entry, rowBuff);
}
file.autoDelete();
}
}
}
private void saveIfPossible(UndoLogRecord r, Data buff) {
if (!r.isStored() && r.canStore()) {
r.save(buff, file, this);
memoryUndo--;
}
} }
/** /**
......
...@@ -515,7 +515,7 @@ public abstract class Table extends SchemaObjectBase { ...@@ -515,7 +515,7 @@ public abstract class Table extends SchemaObjectBase {
} catch (DbException e) { } catch (DbException e) {
if (e.getErrorCode() == ErrorCode.CONCURRENT_UPDATE_1 if (e.getErrorCode() == ErrorCode.CONCURRENT_UPDATE_1
|| e.getErrorCode() == ErrorCode.ROW_NOT_FOUND_WHEN_DELETING_1) { || e.getErrorCode() == ErrorCode.ROW_NOT_FOUND_WHEN_DELETING_1) {
session.rollbackTo(rollback, false); session.rollbackTo(rollback);
session.startStatementWithinTransaction(); session.startStatementWithinTransaction();
rollback = session.setSavepoint(); rollback = session.setSavepoint();
} }
...@@ -534,7 +534,7 @@ public abstract class Table extends SchemaObjectBase { ...@@ -534,7 +534,7 @@ public abstract class Table extends SchemaObjectBase {
addRow(session, n); addRow(session, n);
} catch (DbException e) { } catch (DbException e) {
if (e.getErrorCode() == ErrorCode.CONCURRENT_UPDATE_1) { if (e.getErrorCode() == ErrorCode.CONCURRENT_UPDATE_1) {
session.rollbackTo(rollback, false); session.rollbackTo(rollback);
session.startStatementWithinTransaction(); session.startStatementWithinTransaction();
rollback = session.setSavepoint(); rollback = session.setSavepoint();
} }
......
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.todo;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.tools.DeleteDbFiles;
/**
* A test to reproduce out of memory using a large operation.
*/
public class TestUndoLogMemory {
/**
* Run just this test.
*
* @param args ignored
*/
public static void main(String... args) throws Exception {
TestUndoLogMemory.test(10, "null");
TestUndoLogMemory.test(100, "space(100000)");
// new TestUndoLogMemory().test(100000, "null");
// new TestUndoLogMemory().test(1000, "space(100000)");
}
private static void test(int count, String defaultValue) throws SQLException {
// -Xmx1m -XX:+HeapDumpOnOutOfMemoryError
DeleteDbFiles.execute("data", "test", true);
Connection conn = DriverManager.getConnection(
"jdbc:h2:data/test;large_transactions=true");
Statement stat = conn.createStatement();
stat.execute("set cache_size 32");
stat.execute("SET max_operation_memory 100");
stat.execute("SET max_memory_undo 100");
conn.setAutoCommit(false);
// also a problem: tables without unique index
System.out.println("create--- " + count + " " + defaultValue);
stat.execute("create table test(id int, name varchar default " +
defaultValue + " )");
System.out.println("insert---");
stat.execute("insert into test(id) select x from system_range(1, " +
count + ")");
System.out.println("rollback---");
conn.rollback();
System.out.println("drop---");
stat.execute("drop table test");
System.out.println("create---");
stat.execute("create table test" +
"(id int primary key, name varchar default " +
defaultValue + " )");
// INSERT problem
System.out.println("insert---");
stat.execute(
"insert into test(id) select x from system_range(1, "+count+")");
System.out.println("delete---");
stat.execute("delete from test");
// DELETE problem
System.out.println("insert---");
PreparedStatement prep = conn.prepareStatement(
"insert into test(id) values(?)");
for (int i = 0; i < count; i++) {
prep.setInt(1, i);
prep.execute();
}
System.out.println("delete---");
stat.execute("delete from test");
System.out.println("close---");
conn.close();
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论