Unverified 提交 6d22a935 authored 作者: Andrei Tokar's avatar Andrei Tokar 提交者: GitHub

Merge pull request #1186 from h2database/size-as-long

TransactionMap::sizeAsLong() optimized - temp map eliminated
...@@ -70,6 +70,11 @@ public class TransactionMap<K, V> { ...@@ -70,6 +70,11 @@ public class TransactionMap<K, V> {
public long sizeAsLong() { public long sizeAsLong() {
TransactionStore store = transaction.store; TransactionStore store = transaction.store;
// The purpose of the following loop is to get a coherent picture
// of a state of three independent volatile / atomic variables,
// which they had at some recent moment in time.
// In order to get such a "snapshot", we wait for a moment of silence,
// when none of the variables concurrently changes it's value.
BitSet committingTransactions; BitSet committingTransactions;
MVMap.RootReference mapRootReference; MVMap.RootReference mapRootReference;
MVMap.RootReference undoLogRootReference; MVMap.RootReference undoLogRootReference;
...@@ -79,58 +84,67 @@ public class TransactionMap<K, V> { ...@@ -79,58 +84,67 @@ public class TransactionMap<K, V> {
undoLogRootReference = store.undoLog.getRoot(); undoLogRootReference = store.undoLog.getRoot();
} while(committingTransactions != store.committingTransactions.get() || } while(committingTransactions != store.committingTransactions.get() ||
mapRootReference != map.getRoot()); mapRootReference != map.getRoot());
// Now we have a snapshot, where mapRootReference points to state of the map,
// undoLogRootReference captures the state of undo log
// and committingTransactions mask tells us which of seemingly uncommitted changes
// should be considered as committed.
// Subsequent processing uses this snapshot info only.
Page undoRootPage = undoLogRootReference.root; Page undoRootPage = undoLogRootReference.root;
long undoLogSize = undoRootPage.getTotalCount(); long undoLogSize = undoRootPage.getTotalCount();
Page mapRootPage = mapRootReference.root; Page mapRootPage = mapRootReference.root;
long size = mapRootPage.getTotalCount(); long size = mapRootPage.getTotalCount();
// if we are looking at the map without any uncommitted values
if (undoLogSize == 0) { if (undoLogSize == 0) {
return size; return size;
} }
if (undoLogSize > size) {
// the undo log is larger than the map - // Entries describing removals from the map by this transaction and all transactions,
// count the entries of the map // which are committed but not closed yet,
size = 0; // and antries about additions to the map by other uncommitted transactions were counted,
// but they should not contribute into total count.
if (2 * undoLogSize > size) {
// the undo log is larger than half of the map - scan the entries of the map directly
Cursor<K, VersionedValue> cursor = new Cursor<>(mapRootPage, null); Cursor<K, VersionedValue> cursor = new Cursor<>(mapRootPage, null);
while (cursor.hasNext()) { while(cursor.hasNext()) {
K key = cursor.next(); cursor.next();
VersionedValue data = cursor.getValue(); VersionedValue currentValue = cursor.getValue();
data = getValue(data, committingTransactions); assert currentValue != null;
if (data != null && data.value != null) { long operationId = currentValue.getOperationId();
size++; if (operationId != 0) { // skip committed entries
int txId = TransactionStore.getTransactionId(operationId);
boolean isVisible = txId == transaction.transactionId || committingTransactions.get(txId);
Object v = isVisible ? currentValue.value : currentValue.getCommittedValue();
if (v == null) {
--size;
}
} }
} }
return size; } else {
} // The undo log is much smaller than the map - scan the undo log, and then lookup relevant map entry.
// the undo log is smaller than the map -
// scan the undo log and subtract invisible entries
MVMap<Object, Integer> temp = store.createTempMap();
try {
Cursor<Long, Object[]> cursor = new Cursor<>(undoRootPage, null); Cursor<Long, Object[]> cursor = new Cursor<>(undoRootPage, null);
while (cursor.hasNext()) { while(cursor.hasNext()) {
cursor.next(); cursor.next();
Object[] op = cursor.getValue(); Object op[] = cursor.getValue();
int m = (int) op[0]; if ((int)op[0] == map.getId()) {
if (m != map.getId()) { VersionedValue currentValue = map.get(mapRootPage, op[1]);
// a different map - ignore // If map entry is not there, then we never counted it, in the first place, so skip it.
continue; // This is possible when undo entry exists because it belongs
} // to a committed but not yet closed transaction,
@SuppressWarnings("unchecked") // and it was later deleted by some other already committed and closed transaction.
K key = (K) op[1]; if (currentValue != null) {
VersionedValue data = map.get(mapRootPage, key); // only the last undo entry for any given map key should be considered
data = getValue(data, committingTransactions); long operationId = cursor.getKey();
if (data == null || data.value == null) { if (currentValue.getOperationId() == operationId) {
Integer old = temp.put(key, 1); int txId = TransactionStore.getTransactionId(operationId);
// count each key only once (there might be boolean isVisible = txId == transaction.transactionId || committingTransactions.get(txId);
// multiple Object v = isVisible ? currentValue.value : currentValue.getCommittedValue();
// changes for the same key) if (v == null) {
if (old == null) { --size;
size--; }
}
} }
} }
} }
} finally {
transaction.store.store.removeMap(temp);
} }
return size; return size;
} }
...@@ -623,10 +637,10 @@ public class TransactionMap<K, V> { ...@@ -623,10 +637,10 @@ public class TransactionMap<K, V> {
TransactionStore store = transactionMap.transaction.store; TransactionStore store = transactionMap.transaction.store;
MVMap<K, VersionedValue> map = transactionMap.map; MVMap<K, VersionedValue> map = transactionMap.map;
// The purpose of the following loop is to get a coherent picture // The purpose of the following loop is to get a coherent picture
// of a state of two independent volatile / atomic variables // of a state of two independent volatile / atomic variables,
// which they had at some recent moment in time. // which they had at some recent moment in time.
// In order to get such a "snapshot", we wait for a moment of silence // In order to get such a "snapshot", we wait for a moment of silence,
// when neither of the variables concurrently chenges it's value. // when neither of the variables concurrently changes it's value.
BitSet committingTransactions; BitSet committingTransactions;
MVMap.RootReference mapRootReference; MVMap.RootReference mapRootReference;
do { do {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论