提交 1e01a486 authored 作者: Thomas Mueller's avatar Thomas Mueller

Row level locking for MVCC is now enabled.

上级 5ae2eac5
...@@ -191,7 +191,7 @@ public abstract class Command implements CommandInterface { ...@@ -191,7 +191,7 @@ public abstract class Command implements CommandInterface {
} }
public int executeUpdate() throws SQLException { public int executeUpdate() throws SQLException {
startTime = System.currentTimeMillis(); long start = startTime = System.currentTimeMillis();
Database database = session.getDatabase(); Database database = session.getDatabase();
database.allocateReserveMemory(); database.allocateReserveMemory();
Object sync = database.getMultiThreaded() ? (Object) session : (Object) database; Object sync = database.getMultiThreaded() ? (Object) session : (Object) database;
...@@ -200,15 +200,31 @@ public abstract class Command implements CommandInterface { ...@@ -200,15 +200,31 @@ public abstract class Command implements CommandInterface {
int rollback = session.getLogId(); int rollback = session.getLogId();
session.setCurrentCommand(this, startTime); session.setCurrentCommand(this, startTime);
try { try {
while (true) {
database.checkPowerOff(); database.checkPowerOff();
try { try {
return update(); return update();
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
database.freeReserveMemory(); database.freeReserveMemory();
throw Message.convert(e); throw Message.convert(e);
} catch (SQLException e) {
if (e.getErrorCode() == ErrorCode.CONCURRENT_UPDATE_1) {
long now = System.currentTimeMillis();
if (now - start > session.getLockTimeout()) {
throw e;
}
try {
database.wait(100);
} catch (InterruptedException e1) {
// ignore
}
continue;
}
throw e;
} catch (Throwable e) { } catch (Throwable e) {
throw Message.convert(e); throw Message.convert(e);
} }
}
} catch (SQLException e) { } catch (SQLException e) {
database.exceptionThrown(e, sql); database.exceptionThrown(e, sql);
database.checkPowerOff(); database.checkPowerOff();
......
...@@ -341,13 +341,6 @@ public class Constants { ...@@ -341,13 +341,6 @@ public class Constants {
*/ */
public static final int LOCK_MODE_READ_COMMITTED = 3; public static final int LOCK_MODE_READ_COMMITTED = 3;
/**
* The lock mode that means row level locks are used if possible.
* This lock mode is similar to read committed, but row level locks are
* used instead of table level locks.
*/
public static final int LOCK_MODE_ROW = 4;
/** /**
* The lock mode that means table level locking is used for reads and * The lock mode that means table level locking is used for reads and
* writes. * writes.
...@@ -367,12 +360,6 @@ public class Constants { ...@@ -367,12 +360,6 @@ public class Constants {
*/ */
public static final int LOCK_SLEEP = 1000; public static final int LOCK_SLEEP = 1000;
/**
* The divider used to calculate the minimum log file size as a function of
* the largest file (data file or index file).
*/
public static final long LOG_SIZE_DIVIDER = 10;
/** /**
* The file header used for binary files. * The file header used for binary files.
*/ */
......
...@@ -696,7 +696,6 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -696,7 +696,6 @@ public class JdbcConnection extends TraceObject implements Connection {
transactionIsolationLevel = Connection.TRANSACTION_READ_UNCOMMITTED; transactionIsolationLevel = Connection.TRANSACTION_READ_UNCOMMITTED;
break; break;
case Constants.LOCK_MODE_READ_COMMITTED: case Constants.LOCK_MODE_READ_COMMITTED:
case Constants.LOCK_MODE_ROW:
transactionIsolationLevel = Connection.TRANSACTION_READ_COMMITTED; transactionIsolationLevel = Connection.TRANSACTION_READ_COMMITTED;
break; break;
case Constants.LOCK_MODE_TABLE: case Constants.LOCK_MODE_TABLE:
......
...@@ -274,7 +274,6 @@ public class TableData extends Table implements RecordReader { ...@@ -274,7 +274,6 @@ public class TableData extends Table implements RecordReader {
} }
public void removeRow(Session session, Row row) throws SQLException { public void removeRow(Session session, Row row) throws SQLException {
lastModificationId = database.getNextModificationDataId();
if (database.isMultiVersion()) { if (database.isMultiVersion()) {
if (row.getDeleted()) { if (row.getDeleted()) {
throw Message.getSQLException(ErrorCode.CONCURRENT_UPDATE_1, getName()); throw Message.getSQLException(ErrorCode.CONCURRENT_UPDATE_1, getName());
...@@ -287,6 +286,7 @@ public class TableData extends Table implements RecordReader { ...@@ -287,6 +286,7 @@ public class TableData extends Table implements RecordReader {
throw Message.getSQLException(ErrorCode.CONCURRENT_UPDATE_1, getName()); throw Message.getSQLException(ErrorCode.CONCURRENT_UPDATE_1, getName());
} }
} }
lastModificationId = database.getNextModificationDataId();
int i = indexes.size() - 1; int i = indexes.size() - 1;
try { try {
for (; i >= 0; i--) { for (; i >= 0; i--) {
...@@ -377,7 +377,7 @@ public class TableData extends Table implements RecordReader { ...@@ -377,7 +377,7 @@ public class TableData extends Table implements RecordReader {
} }
} else { } else {
if (lockExclusive == null) { if (lockExclusive == null) {
if (lockMode == Constants.LOCK_MODE_READ_COMMITTED || lockMode == Constants.LOCK_MODE_ROW) { if (lockMode == Constants.LOCK_MODE_READ_COMMITTED) {
if (!database.getMultiThreaded() && !database.isMultiVersion()) { if (!database.getMultiThreaded() && !database.isMultiVersion()) {
// READ_COMMITTED: a read lock is acquired, // READ_COMMITTED: a read lock is acquired,
// but released immediately after the operation // but released immediately after the operation
......
...@@ -75,6 +75,7 @@ import org.h2.test.jdbcx.TestXASimple; ...@@ -75,6 +75,7 @@ import org.h2.test.jdbcx.TestXASimple;
import org.h2.test.mvcc.TestMvcc1; import org.h2.test.mvcc.TestMvcc1;
import org.h2.test.mvcc.TestMvcc2; import org.h2.test.mvcc.TestMvcc2;
import org.h2.test.mvcc.TestMvcc3; import org.h2.test.mvcc.TestMvcc3;
import org.h2.test.mvcc.TestMvccMultiThreaded;
import org.h2.test.rowlock.TestRowLocks; import org.h2.test.rowlock.TestRowLocks;
import org.h2.test.server.TestNestedLoop; import org.h2.test.server.TestNestedLoop;
import org.h2.test.server.TestPgServer; import org.h2.test.server.TestPgServer;
...@@ -272,6 +273,8 @@ java org.h2.test.TestAll timer ...@@ -272,6 +273,8 @@ java org.h2.test.TestAll timer
/* /*
row level locking for mvcc
Run benchmark with the newest versions of other databases. Run benchmark with the newest versions of other databases.
documentation: use 'server mode' not 'remote mode'. documentation: use 'server mode' not 'remote mode'.
...@@ -540,6 +543,7 @@ http://www.w3schools.com/sql/ ...@@ -540,6 +543,7 @@ http://www.w3schools.com/sql/
new TestMvcc1().runTest(this); new TestMvcc1().runTest(this);
new TestMvcc2().runTest(this); new TestMvcc2().runTest(this);
new TestMvcc3().runTest(this); new TestMvcc3().runTest(this);
new TestMvccMultiThreaded().runTest(this);
new TestRowLocks().runTest(this); new TestRowLocks().runTest(this);
// synth // synth
......
...@@ -60,6 +60,17 @@ public abstract class TestBase { ...@@ -60,6 +60,17 @@ public abstract class TestBase {
config.beforeTest(); config.beforeTest();
} }
/**
* Initialize the test configuration using the default settings.
*
* @return itself
*/
public TestBase init() throws Exception {
baseDir = getTestDir("");
this.config = new TestAll();
return this;
}
/** /**
* Initialize the test configuration. * Initialize the test configuration.
* *
...@@ -483,11 +494,11 @@ public abstract class TestBase { ...@@ -483,11 +494,11 @@ public abstract class TestBase {
} }
int al = expected.length(); int al = expected.length();
int bl = actual.length(); int bl = actual.length();
if (al > 400) { if (al > 4000) {
expected = expected.substring(0, 400); expected = expected.substring(0, 4000);
} }
if (bl > 400) { if (bl > 4000) {
actual = actual.substring(0, 400); actual = actual.substring(0, 4000);
} }
fail("Expected: " + expected + " (" + al + ") actual: " + actual + " (" + bl + ")"); fail("Expected: " + expected + " (" + al + ") actual: " + actual + " (" + bl + ")");
} }
......
...@@ -18,8 +18,12 @@ import org.h2.test.TestBase; ...@@ -18,8 +18,12 @@ import org.h2.test.TestBase;
*/ */
public class TestRowLocks extends TestBase { public class TestRowLocks extends TestBase {
Statement s1, s2;
private Connection c1, c2; private Connection c1, c2;
private Statement s1, s2;
public static void main(String[] a) throws Exception {
new TestRowLocks().init().test();
}
public void test() throws Exception { public void test() throws Exception {
testSetMode(); testSetMode();
...@@ -30,10 +34,10 @@ public class TestRowLocks extends TestBase { ...@@ -30,10 +34,10 @@ public class TestRowLocks extends TestBase {
deleteDb("rowLocks"); deleteDb("rowLocks");
c1 = getConnection("rowLocks"); c1 = getConnection("rowLocks");
Statement stat = c1.createStatement(); Statement stat = c1.createStatement();
stat.execute("SET LOCK_MODE 4"); stat.execute("SET LOCK_MODE 2");
ResultSet rs = stat.executeQuery("call lock_mode()"); ResultSet rs = stat.executeQuery("call lock_mode()");
rs.next(); rs.next();
assertEquals("4", rs.getString(1)); assertEquals("2", rs.getString(1));
c1.close(); c1.close();
} }
...@@ -41,40 +45,57 @@ public class TestRowLocks extends TestBase { ...@@ -41,40 +45,57 @@ public class TestRowLocks extends TestBase {
deleteDb("rowLocks"); deleteDb("rowLocks");
c1 = getConnection("rowLocks;MVCC=TRUE"); c1 = getConnection("rowLocks;MVCC=TRUE");
s1 = c1.createStatement(); s1 = c1.createStatement();
s1.execute("SET LOCK_MODE 4"); s1.execute("SET LOCK_TIMEOUT 10000");
s1.execute("CREATE TABLE TEST AS SELECT X ID, 'Hello' NAME FROM SYSTEM_RANGE(1, 3)"); s1.execute("CREATE TABLE TEST AS SELECT X ID, 'Hello' NAME FROM SYSTEM_RANGE(1, 3)");
c1.commit(); c1.commit();
c1.setAutoCommit(false); c1.setAutoCommit(false);
s1.execute("UPDATE TEST SET NAME='Hallo' WHERE ID=1"); s1.execute("UPDATE TEST SET NAME='Hallo' WHERE ID=1");
c2 = getConnection("rowLocks"); c2 = getConnection("rowLocks");
c2.setAutoCommit(false); c2.setAutoCommit(false);
s2 = c2.createStatement(); s2 = c2.createStatement();
ResultSet rs = s1.executeQuery("SELECT NAME FROM TEST WHERE ID=1"); assertEquals("Hallo", getSingleValue(s1, "SELECT NAME FROM TEST WHERE ID=1"));
rs.next(); assertEquals("Hello", getSingleValue(s2, "SELECT NAME FROM TEST WHERE ID=1"));
assertEquals("Hallo", rs.getString(1));
rs = s2.executeQuery("SELECT NAME FROM TEST WHERE ID=1");
rs.next();
assertEquals("Hello", rs.getString(1));
s2.execute("UPDATE TEST SET NAME='Hallo' WHERE ID=2"); s2.execute("UPDATE TEST SET NAME='Hallo' WHERE ID=2");
try { try {
s2.executeUpdate("UPDATE TEST SET NAME='Hallo2' WHERE ID=1"); s2.executeUpdate("UPDATE TEST SET NAME='Hi' WHERE ID=1");
fail(); fail();
} catch (SQLException e) { } catch (SQLException e) {
assertKnownException(e); assertKnownException(e);
} }
c1.commit(); c1.commit();
c2.commit(); c2.commit();
rs = s1.executeQuery("SELECT NAME FROM TEST WHERE ID=1");
rs.next(); assertEquals("Hallo", getSingleValue(s1, "SELECT NAME FROM TEST WHERE ID=1"));
assertEquals("Hallo", rs.getString(1)); assertEquals("Hallo", getSingleValue(s2, "SELECT NAME FROM TEST WHERE ID=1"));
rs = s2.executeQuery("SELECT NAME FROM TEST WHERE ID=1");
rs.next(); s2.execute("UPDATE TEST SET NAME='H1' WHERE ID=1");
assertEquals("Hallo", rs.getString(1)); Thread thread = new Thread() {
public void run() {
try {
s1.execute("UPDATE TEST SET NAME='H2' WHERE ID=1");
} catch (SQLException e) {
e.printStackTrace();
}
}
};
thread.start();
Thread.sleep(100);
c2.commit();
thread.join();
c1.commit();
assertEquals("H2", getSingleValue(s1, "SELECT NAME FROM TEST WHERE ID=1"));
assertEquals("H2", getSingleValue(s2, "SELECT NAME FROM TEST WHERE ID=1"));
c1.close(); c1.close();
c2.close(); c2.close();
} }
private String getSingleValue(Statement stat, String sql) throws Exception {
ResultSet rs = stat.executeQuery(sql);
return rs.next() ? rs.getString(1) : null;
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论