提交 9447abe0 authored 作者: andrei's avatar andrei

Look Ma, no more locks!

上级 05ad3781
......@@ -10,9 +10,9 @@ import org.h2.mvstore.DataUtils;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.Page;
import org.h2.mvstore.type.DataType;
import java.util.BitSet;
import java.util.AbstractMap;
import java.util.BitSet;
import java.util.Iterator;
import java.util.Map;
......@@ -99,77 +99,72 @@ public class TransactionMap<K, V> {
*/
public long sizeAsLong() {
TransactionStore store = transaction.store;
store.rwLock.readLock().lock();
try {
MVMap<Long, Object[]> undo = transaction.store.undoLog;
BitSet committingTransactions;
MVMap.RootReference mapRootReference;
MVMap.RootReference undoLogRootReference;
do {
committingTransactions = store.committingTransactions.get();
mapRootReference = map.getRoot();
undoLogRootReference = store.undoLog.getRoot();
} while(committingTransactions != store.committingTransactions.get() ||
mapRootReference != map.getRoot());
MVMap<Long, Object[]> undo = transaction.store.undoLog;
Page undoRootPage = undoLogRootReference.root;
long undoLogSize = undoRootPage.getTotalCount();
Page mapRootPage = mapRootReference.root;
long size = mapRootPage.getTotalCount();
if (undoLogSize == 0) {
return size;
}
if (undoLogSize > size) {
// the undo log is larger than the map -
// count the entries of the map
size = 0;
Cursor<K, VersionedValue> cursor = map.cursor(null);
while (cursor.hasNext()) {
K key = cursor.next();
VersionedValue data = cursor.getValue();
data = getValue(mapRootPage, undoRootPage, key, readLogId, data, committingTransactions);
if (data != null && data.value != null) {
size++;
}
BitSet committingTransactions;
MVMap.RootReference mapRootReference;
MVMap.RootReference undoLogRootReference;
do {
committingTransactions = store.committingTransactions.get();
mapRootReference = map.getRoot();
undoLogRootReference = store.undoLog.getRoot();
} while(committingTransactions != store.committingTransactions.get() ||
mapRootReference != map.getRoot());
Page undoRootPage = undoLogRootReference.root;
long undoLogSize = undoRootPage.getTotalCount();
Page mapRootPage = mapRootReference.root;
long size = mapRootPage.getTotalCount();
if (undoLogSize == 0) {
return size;
}
if (undoLogSize > size) {
// the undo log is larger than the map -
// count the entries of the map
size = 0;
Cursor<K, VersionedValue> cursor = map.cursor(null);
while (cursor.hasNext()) {
K key = cursor.next();
VersionedValue data = cursor.getValue();
data = getValue(mapRootPage, undoRootPage, key, readLogId, data, committingTransactions);
if (data != null && data.value != null) {
size++;
}
return size;
}
// the undo log is smaller than the map -
// scan the undo log and subtract invisible entries
MVMap<Object, Integer> temp = transaction.store
.createTempMap();
try {
Cursor cursor = new Cursor<Long, Object[]>(undoRootPage, null);
while (cursor.hasNext()) {
cursor.next();
Object[] op = (Object[]) cursor.getValue();
int m = (Integer) op[0];
if (m != mapId) {
// a different map - ignore
continue;
}
@SuppressWarnings("unchecked")
K key = (K) op[1];
VersionedValue data = map.get(mapRootPage, key);
data = getValue(mapRootPage, undoRootPage, key, readLogId, data, committingTransactions);
if (data == null || data.value == null) {
Integer old = temp.put(key, 1);
// count each key only once (there might be
// multiple
// changes for the same key)
if (old == null) {
size--;
}
return size;
}
// the undo log is smaller than the map -
// scan the undo log and subtract invisible entries
MVMap<Object, Integer> temp = transaction.store
.createTempMap();
try {
Cursor cursor = new Cursor<Long, Object[]>(undoRootPage, null);
while (cursor.hasNext()) {
cursor.next();
Object[] op = (Object[]) cursor.getValue();
int m = (Integer) op[0];
if (m != mapId) {
// a different map - ignore
continue;
}
@SuppressWarnings("unchecked")
K key = (K) op[1];
VersionedValue data = map.get(mapRootPage, key);
data = getValue(mapRootPage, undoRootPage, key, readLogId, data, committingTransactions);
if (data == null || data.value == null) {
Integer old = temp.put(key, 1);
// count each key only once (there might be
// multiple
// changes for the same key)
if (old == null) {
size--;
}
}
} finally {
transaction.store.store.removeMap(temp);
}
return size;
} finally {
transaction.store.rwLock.readLock().unlock();
transaction.store.store.removeMap(temp);
}
return size;
}
/**
......@@ -420,25 +415,20 @@ public class TransactionMap<K, V> {
private VersionedValue getValue(K key, long maxLog) {
TransactionStore store = transaction.store;
store.rwLock.readLock().lock();
try {
BitSet committingTransactions;
MVMap.RootReference mapRootReference;
MVMap.RootReference undoLogRootReference;
do {
committingTransactions = store.committingTransactions.get();
mapRootReference = map.getRoot();
undoLogRootReference = store.undoLog.getRoot();
} while(committingTransactions != store.committingTransactions.get() ||
mapRootReference != map.getRoot());
Page undoRootPage = undoLogRootReference.root;
Page mapRootPage = mapRootReference.root;
VersionedValue data = map.get(mapRootPage, key);
return getValue(mapRootPage, undoRootPage, key, maxLog, data, store.committingTransactions.get());
} finally {
store.rwLock.readLock().unlock();
}
BitSet committingTransactions;
MVMap.RootReference mapRootReference;
MVMap.RootReference undoLogRootReference;
do {
committingTransactions = store.committingTransactions.get();
mapRootReference = map.getRoot();
undoLogRootReference = store.undoLog.getRoot();
} while(committingTransactions != store.committingTransactions.get() ||
mapRootReference != map.getRoot());
Page undoRootPage = undoLogRootReference.root;
Page mapRootPage = mapRootReference.root;
VersionedValue data = map.get(mapRootPage, key);
return getValue(mapRootPage, undoRootPage, key, maxLog, data, store.committingTransactions.get());
}
/**
......
......@@ -13,7 +13,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.h2.mvstore.DataUtils;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.MVStore;
......@@ -54,12 +53,6 @@ public class TransactionStore {
*/
final MVMap<Long, Object[]> undoLog;
/**
* the reader/writer lock for the undo-log. Allows us to process multiple
* selects in parallel.
*/
final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
/**
* The map of maps.
*/
......@@ -169,39 +162,34 @@ public class TransactionStore {
store.removeMap(temp);
}
}
rwLock.writeLock().lock();
try {
if (!undoLog.isEmpty()) {
Long key = undoLog.firstKey();
while (key != null) {
int transactionId = getTransactionId(key);
if (!openTransactions.get().get(transactionId)) {
Object[] data = preparedTransactions.get(transactionId);
int status;
String name;
if (data == null) {
if (undoLog.containsKey(getOperationId(transactionId, 0))) {
status = Transaction.STATUS_OPEN;
} else {
status = Transaction.STATUS_COMMITTING;
}
name = null;
if (!undoLog.isEmpty()) {
Long key = undoLog.firstKey();
while (key != null) {
int transactionId = getTransactionId(key);
if (!openTransactions.get().get(transactionId)) {
Object[] data = preparedTransactions.get(transactionId);
int status;
String name;
if (data == null) {
if (undoLog.containsKey(getOperationId(transactionId, 0))) {
status = Transaction.STATUS_OPEN;
} else {
status = (Integer) data[0];
name = (String) data[1];
status = Transaction.STATUS_COMMITTING;
}
long nextTxUndoKey = getOperationId(transactionId + 1, 0);
Long lastUndoKey = undoLog.lowerKey(nextTxUndoKey);
assert lastUndoKey != null;
assert getTransactionId(lastUndoKey) == transactionId;
long logId = getLogId(lastUndoKey) + 1;
registerTransaction(transactionId, status, name, logId, timeoutMillis, 0, listener);
key = undoLog.ceilingKey(nextTxUndoKey);
name = null;
} else {
status = (Integer) data[0];
name = (String) data[1];
}
long nextTxUndoKey = getOperationId(transactionId + 1, 0);
Long lastUndoKey = undoLog.lowerKey(nextTxUndoKey);
assert lastUndoKey != null;
assert getTransactionId(lastUndoKey) == transactionId;
long logId = getLogId(lastUndoKey) + 1;
registerTransaction(transactionId, status, name, logId, timeoutMillis, 0, listener);
key = undoLog.ceilingKey(nextTxUndoKey);
}
}
} finally {
rwLock.writeLock().unlock();
}
init = true;
}
......@@ -277,23 +265,18 @@ public class TransactionStore {
if(!init) {
init();
}
rwLock.readLock().lock();
try {
ArrayList<Transaction> list = new ArrayList<>();
int transactionId = 0;
BitSet bitSet = openTransactions.get();
while((transactionId = bitSet.nextSetBit(transactionId + 1)) > 0) {
Transaction transaction = getTransaction(transactionId);
if(transaction != null) {
if(transaction.getStatus() != Transaction.STATUS_CLOSED) {
list.add(transaction);
}
ArrayList<Transaction> list = new ArrayList<>();
int transactionId = 0;
BitSet bitSet = openTransactions.get();
while((transactionId = bitSet.nextSetBit(transactionId + 1)) > 0) {
Transaction transaction = getTransaction(transactionId);
if(transaction != null) {
if(transaction.getStatus() != Transaction.STATUS_CLOSED) {
list.add(transaction);
}
}
return list;
} finally {
rwLock.readLock().unlock();
}
return list;
}
/**
......@@ -387,21 +370,16 @@ public class TransactionStore {
*/
long addUndoLogRecord(int transactionId, long logId, Object[] undoLogRecord) {
Long undoKey = getOperationId(transactionId, logId);
rwLock.writeLock().lock();
try {
if (logId == 0) {
if (undoLog.containsKey(undoKey)) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_TOO_MANY_OPEN_TRANSACTIONS,
"An old transaction with the same id " +
"is still open: {0}",
transactionId);
}
if (logId == 0) {
if (undoLog.containsKey(undoKey)) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_TOO_MANY_OPEN_TRANSACTIONS,
"An old transaction with the same id " +
"is still open: {0}",
transactionId);
}
undoLog.put(undoKey, undoLogRecord);
} finally {
rwLock.writeLock().unlock();
}
undoLog.put(undoKey, undoLogRecord);
return undoKey;
}
......@@ -413,17 +391,12 @@ public class TransactionStore {
*/
public void removeUndoLogRecord(int transactionId, long logId) {
Long undoKey = getOperationId(transactionId, logId);
rwLock.writeLock().lock();
try {
Object[] old = undoLog.remove(undoKey);
if (old == null) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_TRANSACTION_ILLEGAL_STATE,
"Transaction {0} was concurrently rolled back",
transactionId);
}
} finally {
rwLock.writeLock().unlock();
Object[] old = undoLog.remove(undoKey);
if (old == null) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_TRANSACTION_ILLEGAL_STATE,
"Transaction {0} was concurrently rolled back",
transactionId);
}
}
......@@ -455,8 +428,6 @@ public class TransactionStore {
flipCommittingTransactionsBit(transactionId, true);
CommitDecisionMaker commitDecisionMaker = new CommitDecisionMaker();
// TODO could synchronize on blocks (100 at a time or so)
rwLock.writeLock().lock();
try {
for (long logId = 0; logId < maxLogId; logId++) {
Long undoKey = getOperationId(transactionId, logId);
......@@ -481,7 +452,6 @@ public class TransactionStore {
undoLog.remove(undoKey);
}
} finally {
rwLock.writeLock().unlock();
flipCommittingTransactionsBit(transactionId, false);
}
}
......@@ -635,18 +605,12 @@ public class TransactionStore {
* @param toLogId the log id to roll back to
*/
void rollbackTo(Transaction t, long maxLogId, long toLogId) {
// TODO could synchronize on blocks (100 at a time or so)
rwLock.writeLock().lock();
try {
int transactionId = t.getId();
RollbackDecisionMaker decisionMaker = new RollbackDecisionMaker(this, transactionId, toLogId, t.listener);
for (long logId = maxLogId - 1; logId >= toLogId; logId--) {
Long undoKey = getOperationId(transactionId, logId);
undoLog.operate(undoKey, null, decisionMaker);
decisionMaker.reset();
}
} finally {
rwLock.writeLock().unlock();
int transactionId = t.getId();
RollbackDecisionMaker decisionMaker = new RollbackDecisionMaker(this, transactionId, toLogId, t.listener);
for (long logId = maxLogId - 1; logId >= toLogId; logId--) {
Long undoKey = getOperationId(transactionId, logId);
undoLog.operate(undoKey, null, decisionMaker);
decisionMaker.reset();
}
}
......@@ -667,33 +631,28 @@ public class TransactionStore {
private Change current;
private void fetchNext() {
rwLock.writeLock().lock();
try {
int transactionId = t.getId();
while (logId >= toLogId) {
Long undoKey = getOperationId(transactionId, logId);
Object[] op = undoLog.get(undoKey);
logId--;
if (op == null) {
// partially rolled back: load previous
undoKey = undoLog.floorKey(undoKey);
if (undoKey == null ||
getTransactionId(undoKey) != transactionId) {
break;
}
logId = getLogId(undoKey);
continue;
}
int mapId = ((Integer) op[0]).intValue();
MVMap<Object, VersionedValue> m = openMap(mapId);
if (m != null) { // could be null if map was removed later on
VersionedValue oldValue = (VersionedValue) op[2];
current = new Change(m.getName(), op[1], oldValue == null ? null : oldValue.value);
return;
int transactionId = t.getId();
while (logId >= toLogId) {
Long undoKey = getOperationId(transactionId, logId);
Object[] op = undoLog.get(undoKey);
logId--;
if (op == null) {
// partially rolled back: load previous
undoKey = undoLog.floorKey(undoKey);
if (undoKey == null ||
getTransactionId(undoKey) != transactionId) {
break;
}
logId = getLogId(undoKey);
continue;
}
int mapId = ((Integer) op[0]).intValue();
MVMap<Object, VersionedValue> m = openMap(mapId);
if (m != null) { // could be null if map was removed later on
VersionedValue oldValue = (VersionedValue) op[2];
current = new Change(m.getName(), op[1], oldValue == null ? null : oldValue.value);
return;
}
} finally {
rwLock.writeLock().unlock();
}
current = null;
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论