提交 53c4629a authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore: up to 65535 open transactions are now supported. Previously, the limit…

MVStore: up to 65535 open transactions are now supported. Previously, the limit was at most 65535 transactions between the oldest open and the newest open transaction (which was quite a strange limit).
上级 5064fb56
...@@ -1621,6 +1621,7 @@ This database has the following known limitations: ...@@ -1621,6 +1621,7 @@ This database has the following known limitations:
In that case files are split into files of 1 GB by default. In that case files are split into files of 1 GB by default.
An example database URL is: <code>jdbc:h2:split:~/test</code>. An example database URL is: <code>jdbc:h2:split:~/test</code>.
</li><li>The maximum number of rows per table is 2^64. </li><li>The maximum number of rows per table is 2^64.
</li><li>The maximum number of open transactions is 65535.
</li><li>Main memory requirements: The larger the database, the more main memory is required. </li><li>Main memory requirements: The larger the database, the more main memory is required.
With the current storage mechanism (the page store), With the current storage mechanism (the page store),
the minimum main memory required is around 1 MB for each 8 GB database file size. the minimum main memory required is around 1 MB for each 8 GB database file size.
......
...@@ -17,7 +17,10 @@ Change Log ...@@ -17,7 +17,10 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>The default limit for in-place LOB objects was changed from 128 to 256 bytes. <ul><li>MVStore: up to 65535 open transactions are now supported.
Previously, the limit was at most 65535 transactions between the oldest open and the
newest open transaction (which was quite a strange limit).
</li><li>The default limit for in-place LOB objects was changed from 128 to 256 bytes.
This is because each read creates a reference to a LOB, and maintaining the references This is because each read creates a reference to a LOB, and maintaining the references
is a big overhead. With the higher limit, less references are needed. is a big overhead. With the higher limit, less references are needed.
</li><li>Tables without columns didn't work. </li><li>Tables without columns didn't work.
......
...@@ -89,9 +89,9 @@ public class DataUtils { ...@@ -89,9 +89,9 @@ public class DataUtils {
public static final int ERROR_TRANSACTION_LOCKED = 101; public static final int ERROR_TRANSACTION_LOCKED = 101;
/** /**
* A very old transaction is still open. * There are too many open transactions.
*/ */
public static final int ERROR_TRANSACTION_STILL_OPEN = 102; public static final int ERROR_TOO_MANY_OPEN_TRANSACTIONS = 102;
/** /**
* The transaction store is in an illegal state (for example, not yet * The transaction store is in an illegal state (for example, not yet
......
...@@ -7,6 +7,7 @@ package org.h2.mvstore.db; ...@@ -7,6 +7,7 @@ package org.h2.mvstore.db;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
...@@ -57,10 +58,10 @@ public class TransactionStore { ...@@ -57,10 +58,10 @@ public class TransactionStore {
private final DataType dataType; private final DataType dataType;
private final BitSet openTransactions = new BitSet();
private boolean init; private boolean init;
private int lastTransactionId;
private int maxTransactionId = 0xffff; private int maxTransactionId = 0xffff;
/** /**
...@@ -119,8 +120,10 @@ public class TransactionStore { ...@@ -119,8 +120,10 @@ public class TransactionStore {
} }
synchronized (undoLog) { synchronized (undoLog) {
if (undoLog.size() > 0) { if (undoLog.size() > 0) {
Long key = undoLog.firstKey(); for (Long key : undoLog.keySet()) {
lastTransactionId = getTransactionId(key); int transactionId = getTransactionId(key);
openTransactions.set(transactionId);
}
} }
} }
} }
...@@ -225,10 +228,14 @@ public class TransactionStore { ...@@ -225,10 +228,14 @@ public class TransactionStore {
DataUtils.ERROR_TRANSACTION_ILLEGAL_STATE, DataUtils.ERROR_TRANSACTION_ILLEGAL_STATE,
"Not initialized"); "Not initialized");
} }
int transactionId = ++lastTransactionId; int transactionId = openTransactions.nextClearBit(1);
if (lastTransactionId >= maxTransactionId) { if (transactionId > maxTransactionId) {
lastTransactionId = 0; throw DataUtils.newIllegalStateException(
DataUtils.ERROR_TOO_MANY_OPEN_TRANSACTIONS,
"There are {0} open transactions",
transactionId - 1);
} }
openTransactions.set(transactionId);
int status = Transaction.STATUS_OPEN; int status = Transaction.STATUS_OPEN;
return new Transaction(this, transactionId, status, null, 0); return new Transaction(this, transactionId, status, null, 0);
} }
...@@ -263,7 +270,7 @@ public class TransactionStore { ...@@ -263,7 +270,7 @@ public class TransactionStore {
if (logId == 0) { if (logId == 0) {
if (undoLog.containsKey(undoKey)) { if (undoLog.containsKey(undoKey)) {
throw DataUtils.newIllegalStateException( throw DataUtils.newIllegalStateException(
DataUtils.ERROR_TRANSACTION_STILL_OPEN, DataUtils.ERROR_TOO_MANY_OPEN_TRANSACTIONS,
"An old transaction with the same id " + "An old transaction with the same id " +
"is still open: {0}", "is still open: {0}",
t.getId()); t.getId());
...@@ -435,6 +442,7 @@ public class TransactionStore { ...@@ -435,6 +442,7 @@ public class TransactionStore {
preparedTransactions.remove(t.getId()); preparedTransactions.remove(t.getId());
} }
t.setStatus(Transaction.STATUS_CLOSED); t.setStatus(Transaction.STATUS_CLOSED);
openTransactions.clear(t.transactionId);
if (store.getAutoCommitDelay() == 0) { if (store.getAutoCommitDelay() == 0) {
store.commit(); store.commit();
return; return;
......
...@@ -49,6 +49,7 @@ public class TestMVTableEngine extends TestBase { ...@@ -49,6 +49,7 @@ public class TestMVTableEngine extends TestBase {
@Override @Override
public void test() throws Exception { public void test() throws Exception {
testManyTransactions();
testAppendOnly(); testAppendOnly();
testLowRetentionTime(); testLowRetentionTime();
testOldAndNew(); testOldAndNew();
...@@ -81,6 +82,23 @@ public class TestMVTableEngine extends TestBase { ...@@ -81,6 +82,23 @@ public class TestMVTableEngine extends TestBase {
testLocking(); testLocking();
testSimple(); testSimple();
} }
private void testManyTransactions() throws Exception {
deleteDb("testManyTransactions");
Connection conn = getConnection("testManyTransactions");
Statement stat = conn.createStatement();
stat.execute("create table test()");
conn.setAutoCommit(false);
stat.execute("insert into test values()");
Connection conn2 = getConnection("testManyTransactions");
Statement stat2 = conn2.createStatement();
for (long i = 0; i < 100000; i++) {
stat2.execute("insert into test values()");
}
conn2.close();
conn.close();
}
private void testAppendOnly() throws Exception { private void testAppendOnly() throws Exception {
deleteDb("testAppendOnly"); deleteDb("testAppendOnly");
......
...@@ -154,15 +154,21 @@ public class TestTransactionStore extends TestBase { ...@@ -154,15 +154,21 @@ public class TestTransactionStore extends TestBase {
ts = new TransactionStore(s); ts = new TransactionStore(s);
ts.init(); ts.init();
ts.setMaxTransactionId(16); ts.setMaxTransactionId(16);
ArrayList<Transaction> openList = new ArrayList<Transaction>();
for (int i = 0, j = 1; i < 64; i++) { for (int i = 0, j = 1; i < 64; i++) {
Transaction t = ts.begin(); Transaction t = ts.begin();
openList.add(t);
assertEquals(j, t.getId()); assertEquals(j, t.getId());
t.commit();
j++; j++;
if (j > 16) { if (j > 16) {
j = 1; j = 1;
} }
if (openList.size() >= 16) {
t = openList.remove(0);
t.commit();
}
} }
s = MVStore.open(null); s = MVStore.open(null);
ts = new TransactionStore(s); ts = new TransactionStore(s);
ts.init(); ts.init();
...@@ -170,10 +176,10 @@ public class TestTransactionStore extends TestBase { ...@@ -170,10 +176,10 @@ public class TestTransactionStore extends TestBase {
ArrayList<Transaction> fifo = New.arrayList(); ArrayList<Transaction> fifo = New.arrayList();
int open = 0; int open = 0;
for (int i = 0; i < 64; i++) { for (int i = 0; i < 64; i++) {
Transaction t = ts.begin(); Transaction t = null;
if (open >= 16) { if (open >= 16) {
try { try {
t.openMap("data").put(i, i); t = ts.begin();
fail(); fail();
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
// expected - too many open // expected - too many open
...@@ -182,9 +188,10 @@ public class TestTransactionStore extends TestBase { ...@@ -182,9 +188,10 @@ public class TestTransactionStore extends TestBase {
first.commit(); first.commit();
open--; open--;
} }
t = ts.begin();
t.openMap("data").put(i, i);
fifo.add(t); fifo.add(t);
open++; open++;
t.openMap("data").put(i, i);
} }
s.close(); s.close();
} }
...@@ -514,6 +521,8 @@ public class TestTransactionStore extends TestBase { ...@@ -514,6 +521,8 @@ public class TestTransactionStore extends TestBase {
assertEquals("first transaction", txOld.getName()); assertEquals("first transaction", txOld.getName());
txOld.prepare(); txOld.prepare();
assertEquals(Transaction.STATUS_PREPARED, txOld.getStatus()); assertEquals(Transaction.STATUS_PREPARED, txOld.getStatus());
txOld = list.get(1);
txOld.commit();
s.commit(); s.commit();
s.close(); s.close();
...@@ -522,6 +531,7 @@ public class TestTransactionStore extends TestBase { ...@@ -522,6 +531,7 @@ public class TestTransactionStore extends TestBase {
ts.init(); ts.init();
tx = ts.begin(); tx = ts.begin();
m = tx.openMap("test"); m = tx.openMap("test");
m.put("3", "Test");
assertEquals(2, tx.getId()); assertEquals(2, tx.getId());
list = ts.getOpenTransactions(); list = ts.getOpenTransactions();
assertEquals(2, list.size()); assertEquals(2, list.size());
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论