Unverified 提交 1505ffe8 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1222 from h2database/leftovers-handling

Leftovers handling
...@@ -593,28 +593,23 @@ public class Select extends Query { ...@@ -593,28 +593,23 @@ public class Select extends Query {
limitRows += offset; limitRows += offset;
} }
} }
ArrayList<Row> forUpdateRows = null; ArrayList<Row> forUpdateRows = this.isForUpdateMvcc ? Utils.<Row>newSmallArrayList() : null;
boolean lockRows = this.isForUpdateMvcc;
if (lockRows) {
forUpdateRows = Utils.newSmallArrayList();
}
int sampleSize = getSampleSizeValue(session); int sampleSize = getSampleSizeValue(session);
LazyResultQueryFlat lazyResult = new LazyResultQueryFlat(expressionArray, LazyResultQueryFlat lazyResult = new LazyResultQueryFlat(expressionArray,
sampleSize, columnCount); sampleSize, columnCount);
if (result == null) { if (result == null) {
return lazyResult; return lazyResult;
} }
while (lazyResult.next()) { if (sort != null && !sortUsingIndex || limitRows <= 0) {
if (lockRows) { limitRows = Long.MAX_VALUE;
}
while (result.getRowCount() < limitRows && lazyResult.next()) {
if (forUpdateRows != null) {
topTableFilter.lockRowAdd(forUpdateRows); topTableFilter.lockRowAdd(forUpdateRows);
} }
result.addRow(lazyResult.currentRow()); result.addRow(lazyResult.currentRow());
if ((sort == null || sortUsingIndex) && limitRows > 0 &&
result.getRowCount() >= limitRows) {
break;
}
} }
if (lockRows) { if (forUpdateRows != null) {
topTableFilter.lockRows(forUpdateRows); topTableFilter.lockRows(forUpdateRows);
} }
return null; return null;
......
...@@ -2264,13 +2264,13 @@ public class Database implements DataHandler { ...@@ -2264,13 +2264,13 @@ public class Database implements DataHandler {
public void setLockMode(int lockMode) { public void setLockMode(int lockMode) {
switch (lockMode) { switch (lockMode) {
case Constants.LOCK_MODE_OFF: case Constants.LOCK_MODE_OFF:
if (multiThreaded) { if (multiThreaded && !isMVStore()) {
// currently the combination of LOCK_MODE=0 and MULTI_THREADED // currently the combination of MVCC=FALSE, LOCK_MODE=0 and MULTI_THREADED
// is not supported. also see code in // is not supported. also see code in
// JdbcDatabaseMetaData#supportsTransactionIsolationLevel(int) // JdbcDatabaseMetaData#supportsTransactionIsolationLevel(int)
throw DbException.get( throw DbException.get(
ErrorCode.UNSUPPORTED_SETTING_COMBINATION, ErrorCode.UNSUPPORTED_SETTING_COMBINATION,
"LOCK_MODE=0 & MULTI_THREADED"); "MVCC=FALSE & LOCK_MODE=0 & MULTI_THREADED");
} }
break; break;
case Constants.LOCK_MODE_READ_COMMITTED: case Constants.LOCK_MODE_READ_COMMITTED:
......
...@@ -30,9 +30,11 @@ final class RollbackDecisionMaker extends MVMap.DecisionMaker<Object[]> { ...@@ -30,9 +30,11 @@ final class RollbackDecisionMaker extends MVMap.DecisionMaker<Object[]> {
@Override @Override
public MVMap.Decision decide(Object[] existingValue, Object[] providedValue) { public MVMap.Decision decide(Object[] existingValue, Object[] providedValue) {
assert decision == null; assert decision == null;
if (existingValue == null) {
// normally existingValue will always be there except of db initialization // normally existingValue will always be there except of db initialization
// where some undo log entry was captured on disk but actual map entry was not // where some undo log entry was captured on disk but actual map entry was not
if (existingValue != null ) { decision = MVMap.Decision.ABORT;
} else {
VersionedValue valueToRestore = (VersionedValue) existingValue[2]; VersionedValue valueToRestore = (VersionedValue) existingValue[2];
long operationId; long operationId;
if (valueToRestore == null || if (valueToRestore == null ||
...@@ -47,8 +49,8 @@ final class RollbackDecisionMaker extends MVMap.DecisionMaker<Object[]> { ...@@ -47,8 +49,8 @@ final class RollbackDecisionMaker extends MVMap.DecisionMaker<Object[]> {
listener.onRollback(map, key, previousValue, valueToRestore); listener.onRollback(map, key, previousValue, valueToRestore);
} }
} }
}
decision = MVMap.Decision.REMOVE; decision = MVMap.Decision.REMOVE;
}
return decision; return decision;
} }
......
...@@ -18,7 +18,7 @@ public abstract class TxDecisionMaker extends MVMap.DecisionMaker<VersionedValue ...@@ -18,7 +18,7 @@ public abstract class TxDecisionMaker extends MVMap.DecisionMaker<VersionedValue
final Object value; final Object value;
private final Transaction transaction; private final Transaction transaction;
long undoKey; long undoKey;
private long lastOperationId; protected long lastOperationId;
private Transaction blockingTransaction; private Transaction blockingTransaction;
private MVMap.Decision decision; private MVMap.Decision decision;
...@@ -54,16 +54,18 @@ public abstract class TxDecisionMaker extends MVMap.DecisionMaker<VersionedValue ...@@ -54,16 +54,18 @@ public abstract class TxDecisionMaker extends MVMap.DecisionMaker<VersionedValue
// should wait on blockingTransaction that was determined earlier // should wait on blockingTransaction that was determined earlier
decision = MVMap.Decision.ABORT; decision = MVMap.Decision.ABORT;
} else if(id == lastOperationId) { } else if(id == lastOperationId) {
// There is no transaction with that id, so we've retried it just before, // There is no transaction with that id, and we've tried it just before,
// but map root has not changed (which must be the case if we just missed a closed transaction), // but map root has not changed (which must be the case if we just missed a closed transaction),
// therefore we came back here again. // therefore we came back here again.
// Now we assume it's a leftover after unclean shutdown (map update was written but not undo log), // Now we assume it's a leftover after unclean shutdown (map update was written but not undo log),
// and will effectively roll it back (just overwrite). // and will effectively roll it back (just assume committed value and overwrite).
Object committedValue = existingValue.getCommittedValue(); Object committedValue = existingValue.getCommittedValue();
logIt(committedValue == null ? null : VersionedValue.getInstance(committedValue)); logIt(committedValue == null ? null : VersionedValue.getInstance(committedValue));
decision = MVMap.Decision.PUT; decision = MVMap.Decision.PUT;
} else { } else {
// condition above means transaction has been committed/rolled back and closed by now // transaction has been committed/rolled back and is closed by now, so
// we can retry immediately and either that entry become committed
// or we'll hit case above
decision = MVMap.Decision.REPEAT; decision = MVMap.Decision.REPEAT;
lastOperationId = id; lastOperationId = id;
} }
...@@ -162,16 +164,29 @@ public abstract class TxDecisionMaker extends MVMap.DecisionMaker<VersionedValue ...@@ -162,16 +164,29 @@ public abstract class TxDecisionMaker extends MVMap.DecisionMaker<VersionedValue
// and therefore will be committed soon // and therefore will be committed soon
logIt(null); logIt(null);
return setDecision(MVMap.Decision.PUT); return setDecision(MVMap.Decision.PUT);
} else if(fetchTransaction(blockingId) == null) { } else if(fetchTransaction(blockingId) != null) {
// map already has specified key from uncommitted // this entry comes from a different transaction, and this transaction is not committed yet
// at the time transaction, which is closed by now // should wait on blockingTransaction that was determined earlier and then try again
// we can retry right away return setDecision(MVMap.Decision.ABORT);
return setDecision(MVMap.Decision.REPEAT); } else if(id == lastOperationId) {
} else { // There is no transaction with that id, and we've tried it just before,
// map already has specified key from uncommitted transaction // but map root has not changed (which must be the case if we just missed a closed transaction),
// we need to wait for it to close and then try again // therefore we came back here again.
// Now we assume it's a leftover after unclean shutdown (map update was written but not undo log),
// and will effectively roll it back (just assume committed value and overwrite).
Object committedValue = existingValue.getCommittedValue();
if(committedValue != null) {
return setDecision(MVMap.Decision.ABORT); return setDecision(MVMap.Decision.ABORT);
} }
logIt(null);
return setDecision(MVMap.Decision.PUT);
} else {
// transaction has been committed/rolled back and is closed by now, so
// we can retry immediately and either that entry become committed
// or we'll hit case above
lastOperationId = id;
return setDecision(MVMap.Decision.REPEAT);
}
} }
} }
} }
......
...@@ -74,7 +74,7 @@ public class TestScalability implements Database.DatabaseTest { ...@@ -74,7 +74,7 @@ public class TestScalability implements Database.DatabaseTest {
ArrayList<Database> dbs = new ArrayList<>(); ArrayList<Database> dbs = new ArrayList<>();
int id = 1; int id = 1;
final String h2Url = "jdbc:h2:./data/test;" + final String h2Url = "jdbc:h2:./data/test;" +
"LOCK_TIMEOUT=10000;MV_STORE=FALSE;LOCK_MODE=3"; "LOCK_TIMEOUT=10000;MV_STORE=FALSE";
dbs.add(createDbEntry(id++, "H2", 1, h2Url)); dbs.add(createDbEntry(id++, "H2", 1, h2Url));
dbs.add(createDbEntry(id++, "H2", 2, h2Url)); dbs.add(createDbEntry(id++, "H2", 2, h2Url));
dbs.add(createDbEntry(id++, "H2", 4, h2Url)); dbs.add(createDbEntry(id++, "H2", 4, h2Url));
...@@ -84,7 +84,7 @@ public class TestScalability implements Database.DatabaseTest { ...@@ -84,7 +84,7 @@ public class TestScalability implements Database.DatabaseTest {
dbs.add(createDbEntry(id++, "H2", 64, h2Url)); dbs.add(createDbEntry(id++, "H2", 64, h2Url));
final String mvUrl = "jdbc:h2:./data/mvTest;" + final String mvUrl = "jdbc:h2:./data/mvTest;" +
"LOCK_TIMEOUT=10000;MULTI_THREADED=1"; "LOCK_TIMEOUT=10000;MULTI_THREADED=1;LOCK_MODE=0";
dbs.add(createDbEntry(id++, "MV", 1, mvUrl)); dbs.add(createDbEntry(id++, "MV", 1, mvUrl));
dbs.add(createDbEntry(id++, "MV", 2, mvUrl)); dbs.add(createDbEntry(id++, "MV", 2, mvUrl));
dbs.add(createDbEntry(id++, "MV", 4, mvUrl)); dbs.add(createDbEntry(id++, "MV", 4, mvUrl));
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论