提交 a77eb02a authored 作者: andrei's avatar andrei

remove batch API for now

fix issue #999
上级 45a1bba8
...@@ -21,14 +21,8 @@ import org.h2.mvstore.type.StringDataType; ...@@ -21,14 +21,8 @@ import org.h2.mvstore.type.StringDataType;
/** /**
* A stored map. * A stored map.
* <p> * <p>
* Read operations can happen concurrently with all other * All read and write operations can happen concurrently with all other
* operations, without risk of corruption. * operations, without risk of corruption.
* <p>
* Write operations first read the relevant area from disk to memory
* concurrently, and only then modify the data. The in-memory part of write
* operations is synchronized. For scalable concurrent in-memory write
* operations, the map should be split into multiple smaller sub-maps that are
* then synchronized independently.
* *
* @param <K> the key class * @param <K> the key class
* @param <V> the value class * @param <V> the value class
...@@ -887,7 +881,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -887,7 +881,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* Forget those old versions that are no longer needed. * Forget those old versions that are no longer needed.
* @param rootReference to inspect * @param rootReference to inspect
*/ */
void removeUnusedOldVersions(RootReference rootReference) { private void removeUnusedOldVersions(RootReference rootReference) {
long oldest = store.getOldestVersionToKeep(); long oldest = store.getOldestVersionToKeep();
// We are trying to keep at least one previous version (if any) here. // We are trying to keep at least one previous version (if any) here.
// This is not really necessary, just need to mimic existing // This is not really necessary, just need to mimic existing
...@@ -1129,7 +1123,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1129,7 +1123,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
return Page.createEmptyLeaf(this); return Page.createEmptyLeaf(this);
} }
public Page createEmptyNode() { protected Page createEmptyNode() {
return Page.createEmptyNode(this); return Page.createEmptyNode(this);
} }
...@@ -1404,86 +1398,83 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1404,86 +1398,83 @@ public class MVMap<K, V> extends AbstractMap<K, V>
} }
} }
public interface EntryProcessor<K,V> { public enum Decision { ABORT, REMOVE, PUT }
boolean process(K key, V value);
}
@SuppressWarnings("unchecked") public abstract static class DecisionMaker<V>
public static <K,V> void process(Page root, K from, EntryProcessor<K,V> entryProcessor) { {
CursorPos cursorPos = Cursor.traverseDown(root, from); public static final DecisionMaker<Object> DEFAULT = new DecisionMaker<Object>() {
CursorPos keeper = null; @Override
while (true) { public Decision decide(Object existingValue, Object providedValue) {
Page page = cursorPos.page; return providedValue == null ? Decision.REMOVE : Decision.PUT;
int index = cursorPos.index;
if (index >= (page.isLeaf() ? page.getKeyCount() : root.map.getChildPageCount(page))) {
CursorPos tmp = cursorPos;
cursorPos = cursorPos.parent;
tmp.parent = keeper;
keeper = tmp;
if(cursorPos == null) {
return;
}
} else {
while (!page.isLeaf()) {
page = page.getChildPage(index);
if (keeper == null) {
cursorPos = new CursorPos(page, 0, cursorPos);
} else {
CursorPos tmp = keeper;
keeper = keeper.parent;
tmp.parent = cursorPos;
tmp.page = page;
tmp.index = 0;
cursorPos = tmp;
}
index = 0;
}
K key = (K) page.getKey(index);
V value = (V) page.getValue(index);
if(entryProcessor.process(key, value)) {
return;
}
} }
++cursorPos.index;
}
} @Override
public String toString() {
return "default";
}
};
public interface LeafProcessor public static final DecisionMaker<Object> PUT = new DecisionMaker<Object>() {
{ @Override
CursorPos locate(Page rootPage); public Decision decide(Object existingValue, Object providedValue) {
Page[] process(CursorPos pos); return Decision.PUT;
CursorPos locateNext(Page rootPage); }
void stepBack();
void confirmSuccess(); @Override
public String toString() {
return "put";
}
};
public static final DecisionMaker<Object> REMOVE = new DecisionMaker<Object>() {
@Override
public Decision decide(Object existingValue, Object providedValue) {
return Decision.REMOVE;
}
@Override
public String toString() {
return "remove";
}
};
private static final DecisionMaker<Object> IF_ABSENT = new DecisionMaker<Object>() {
@Override
public Decision decide(Object existingValue, Object providedValue) {
return existingValue == null ? Decision.PUT : Decision.ABORT;
}
@Override
public String toString() {
return "if_absent";
}
};
private static final DecisionMaker<Object> IF_PRESENT = new DecisionMaker<Object>() {
@Override
public Decision decide(Object existingValue, Object providedValue) {
return existingValue != null ? Decision.PUT : Decision.ABORT;
}
@Override
public String toString() {
return "if_present";
}
};
public abstract Decision decide(V existingValue, V providedValue);
public <T extends V> T selectValue(T existingValue, T providedValue) {
return providedValue;
}
public void reset() {}
} }
public final void operateBatch(LeafProcessor processor) { public V operate(K key, V value, DecisionMaker<? super V> decisionMaker) {
beforeWrite(); beforeWrite();
int attempt = 0; int attempt = 0;
RootReference rootReference = getRoot();
RootReference oldRootReference = null; RootReference oldRootReference = null;
CursorPos pos;
while(true) { while(true) {
if (rootReference.semaphore) { RootReference rootReference = getRoot();
Thread.yield();
rootReference = getRoot();
continue;
}
pos = processor.locate(rootReference.root);
if (pos == null) {
return;
}
Page replacement[] = processor.process(pos);
if (replacement == null) {
if(rootReference != getRoot()) {
processor.stepBack();
rootReference = getRoot();
continue;
}
return;
}
int contention = 0; int contention = 0;
if (oldRootReference != null) { if (oldRootReference != null) {
long updateAttemptCounter = rootReference.updateAttemptCounter - oldRootReference.updateAttemptCounter; long updateAttemptCounter = rootReference.updateAttemptCounter - oldRootReference.updateAttemptCounter;
...@@ -1495,67 +1486,85 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1495,67 +1486,85 @@ public class MVMap<K, V> extends AbstractMap<K, V>
} }
oldRootReference = rootReference; oldRootReference = rootReference;
++attempt; ++attempt;
CursorPos tip = pos; CursorPos pos = traverseDown(rootReference.root, key);
Page p = pos.page; Page p = pos.page;
int index = pos.index;
CursorPos tip = pos;
pos = pos.parent; pos = pos.parent;
final V result = index < 0 ? null : (V)p.getValue(index);
Decision decision = decisionMaker.decide(result, value);
int unsavedMemory = 0; int unsavedMemory = 0;
boolean needUnlock = false; boolean needUnlock = false;
try { try {
if (attempt > 4 && !(needUnlock = lockRoot(processor, rootReference, attempt, contention))) { switch (decision) {
processor.stepBack(); case ABORT:
rootReference = getRoot(); if(rootReference != getRoot()) {
continue; decisionMaker.reset();
} continue;
int index; }
switch (replacement.length) { return result;
case 0: case REMOVE: {
if (pos != null) { if (index < 0) {
p = pos.page; return null;
index = pos.index; }
pos = pos.parent; if (attempt > 2 && !(needUnlock = lockRoot(decisionMaker, rootReference, attempt, contention))) {
assert p.getKeyCount() > 0; continue;
if (p.getKeyCount() == 1) { }
assert index <= 1; if (p.getTotalCount() == 1 && pos != null) {
p = p.getChildPage(1 - index); p = pos.page;
break; index = pos.index;
pos = pos.parent;
if (p.getKeyCount() == 1) {
assert index <= 1;
p = p.getChildPage(1 - index);
break;
}
assert p.getKeyCount() > 1;
} }
p = p.copy(); p = p.copy();
p.remove(index); p.remove(index);
} else { break;
p = createEmptyLeaf();
} }
break; case PUT: {
case 1: if (attempt > 2 && !(needUnlock = lockRoot(decisionMaker, rootReference, attempt, contention))) {
p = replacement[0]; continue;
int keyCount;
while ((keyCount = p.getKeyCount()) > store.getKeysPerPage() || p.getMemory() > store.getMaxPageSize()
&& keyCount > (p.isLeaf() ? 1 : 2)) {
long totalCount = p.getTotalCount();
int at = keyCount >> 1;
Object k = p.getKey(at);
Page split = p.split(at);
unsavedMemory += p.getMemory();
unsavedMemory += split.getMemory();
if (pos == null) {
Object keys[] = { k };
Page.PageReference children[] = {
new Page.PageReference(p),
new Page.PageReference(split)
};
p = Page.create(this, keys, null, children, totalCount, 0);
break;
} }
Page c = p; value = decisionMaker.selectValue(result, value);
p = pos.page;
index = pos.index;
pos = pos.parent;
p = p.copy(); p = p.copy();
p.setChild(index, split); if (index < 0) {
p.insertNode(index, k, c); p.insertLeaf(-index - 1, key, value);
int keyCount;
while ((keyCount = p.getKeyCount()) > store.getKeysPerPage() || p.getMemory() > store.getMaxPageSize()
&& keyCount > (p.isLeaf() ? 1 : 2)) {
long totalCount = p.getTotalCount();
int at = keyCount >> 1;
Object k = p.getKey(at);
Page split = p.split(at);
unsavedMemory += p.getMemory();
unsavedMemory += split.getMemory();
if (pos == null) {
Object keys[] = { k };
Page.PageReference children[] = {
new Page.PageReference(p),
new Page.PageReference(split)
};
p = Page.create(this, keys, null, children, totalCount, 0);
break;
}
Page c = p;
p = pos.page;
index = pos.index;
pos = pos.parent;
p = p.copy();
p.setChild(index, split);
p.insertNode(index, k, c);
}
} else {
p.setValue(index, value);
}
break;
} }
default:
break;
} }
unsavedMemory += p.getMemory(); unsavedMemory += p.getMemory();
while (pos != null) { while (pos != null) {
...@@ -1566,15 +1575,11 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1566,15 +1575,11 @@ public class MVMap<K, V> extends AbstractMap<K, V>
unsavedMemory += p.getMemory(); unsavedMemory += p.getMemory();
pos = pos.parent; pos = pos.parent;
} }
if ((pos = processor.locateNext(p)) != null) {
continue; // Multi-node batch operation
}
if(needUnlock) { if(needUnlock) {
unlockRoot(p, attempt); unlockRoot(p, attempt);
needUnlock = false; needUnlock = false;
} else if(!updateRoot(rootReference, p, attempt)) { } else if(!updateRoot(rootReference, p, attempt)) {
processor.stepBack(); decisionMaker.reset();
rootReference = getRoot();
continue; continue;
} }
while (tip != null) { while (tip != null) {
...@@ -1584,8 +1589,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1584,8 +1589,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
if (store.getFileStore() != null) { if (store.getFileStore() != null) {
store.registerUnsavedPage(unsavedMemory); store.registerUnsavedPage(unsavedMemory);
} }
processor.confirmSuccess(); return result;
break;
} finally { } finally {
if(needUnlock) { if(needUnlock) {
unlockRoot(rootReference.root, attempt); unlockRoot(rootReference.root, attempt);
...@@ -1594,12 +1598,12 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1594,12 +1598,12 @@ public class MVMap<K, V> extends AbstractMap<K, V>
} }
} }
private boolean lockRoot(LeafProcessor processor, RootReference rootReference, private boolean lockRoot(DecisionMaker<? super V> decisionMaker, RootReference rootReference,
int attempt, int contention) { int attempt, int contention) {
boolean success = root.compareAndSet(rootReference, new RootReference(rootReference)); boolean success = lockRoot(rootReference);
if (!success) { if (!success) {
processor.stepBack(); decisionMaker.reset();
if(attempt > 8) { if(attempt > 4) {
if (attempt <= 24) { if (attempt <= 24) {
Thread.yield(); Thread.yield();
} else { } else {
...@@ -1612,6 +1616,11 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1612,6 +1616,11 @@ public class MVMap<K, V> extends AbstractMap<K, V>
return success; return success;
} }
private boolean lockRoot(RootReference rootReference) {
return !rootReference.semaphore
&& root.compareAndSet(rootReference, new RootReference(rootReference));
}
private void unlockRoot(Page newRoot, int attempt) { private void unlockRoot(Page newRoot, int attempt) {
boolean success; boolean success;
do { do {
...@@ -1635,154 +1644,6 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1635,154 +1644,6 @@ public class MVMap<K, V> extends AbstractMap<K, V>
return new CursorPos(p, p.binarySearch(key), pos); return new CursorPos(p, p.binarySearch(key), pos);
} }
public static final class SingleDecisionMaker<K,V> implements LeafProcessor
{
private final DecisionMaker<? super V> decisionMaker;
private final K key;
private final V value;
private V result;
public SingleDecisionMaker(K key, V value, DecisionMaker<? super V> decisionMaker) {
this.decisionMaker = decisionMaker;
this.key = key;
this.value = value;
}
@Override
public CursorPos locate(Page rootPage) {
return traverseDown(rootPage, key);
}
@Override
public CursorPos locateNext(Page rootPage) {
return null;
}
@Override
public void stepBack() {
decisionMaker.reset();
}
@Override
public void confirmSuccess() {}
@SuppressWarnings("unchecked")
@Override
public Page[] process(CursorPos pos) {
Page leaf = pos.page;
int index = pos.index;
result = index < 0 ? null : (V)leaf.getValue(index);
Decision decision = decisionMaker.decide(result, value);
switch (decision) {
case ABORT:
return null;
case REMOVE: {
if (index < 0) {
return null;
}
if (leaf.getTotalCount() == 1) {
return new Page[0];
}
leaf = leaf.copy();
leaf.remove(index);
return new Page[] { leaf };
}
case PUT: {
V v = decisionMaker.selectValue(result, value);
leaf = leaf.copy();
if (index < 0) {
leaf.insertLeaf(-index - 1, key, v);
} else {
leaf.setValue(index, v);
}
return new Page[] { leaf };
}
default:
return null;
}
}
public V getResult() {
return result;
}
}
public enum Decision { ABORT, REMOVE, PUT }
public abstract static class DecisionMaker<V> {
public static final DecisionMaker<Object> DEFAULT = new DecisionMaker<Object>() {
@Override
public Decision decide(Object existingValue, Object providedValue) {
return providedValue == null ? Decision.REMOVE : Decision.PUT;
}
@Override
public String toString() {
return "default";
}
};
public static final DecisionMaker<Object> PUT = new DecisionMaker<Object>() {
@Override
public Decision decide(Object existingValue, Object providedValue) {
return Decision.PUT;
}
@Override
public String toString() {
return "put";
}
};
public static final DecisionMaker<Object> REMOVE = new DecisionMaker<Object>() {
@Override
public Decision decide(Object existingValue, Object providedValue) {
return Decision.REMOVE;
}
@Override
public String toString() {
return "remove";
}
};
private static final DecisionMaker<Object> IF_ABSENT = new DecisionMaker<Object>() {
@Override
public Decision decide(Object existingValue, Object providedValue) {
return existingValue == null ? Decision.PUT : Decision.ABORT;
}
@Override
public String toString() {
return "if_absent";
}
};
private static final DecisionMaker<Object> IF_PRESENT = new DecisionMaker<Object>() {
@Override
public Decision decide(Object existingValue, Object providedValue) {
return existingValue != null ? Decision.PUT : Decision.ABORT;
}
@Override
public String toString() {
return "if_present";
}
};
public abstract Decision decide(V existingValue, V providedValue);
public <T extends V> T selectValue(T existingValue, T providedValue) {
return providedValue;
}
public void reset() {}
}
public V operate(K key, V value, DecisionMaker<? super V> decisionMaker) {
SingleDecisionMaker<K, V> processor = new SingleDecisionMaker<>(key, value, decisionMaker);
operateBatch(processor);
return processor.getResult();
}
private static final class EqualsDecisionMaker<V> extends DecisionMaker<V> { private static final class EqualsDecisionMaker<V> extends DecisionMaker<V> {
private final DataType dataType; private final DataType dataType;
private final V expectedValue; private final V expectedValue;
......
...@@ -114,18 +114,16 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -114,18 +114,16 @@ public class MVPrimaryIndex extends BaseIndex {
TransactionMap<Value, Value> map = getMap(session); TransactionMap<Value, Value> map = getMap(session);
Value key = ValueLong.get(row.getKey()); Value key = ValueLong.get(row.getKey());
Value old = map.getLatest(key);
if (old != null) {
String sql = "PRIMARY KEY ON " + table.getSQL();
if (mainIndexColumn >= 0 && mainIndexColumn < indexColumns.length) {
sql += "(" + indexColumns[mainIndexColumn].getSQL() + ")";
}
DbException e = DbException.get(ErrorCode.DUPLICATE_KEY_1, sql);
e.setSource(this);
throw e;
}
try { try {
map.put(key, ValueArray.get(row.getValueList())); if (map.put(key, ValueArray.get(row.getValueList())) != null) {
String sql = "PRIMARY KEY ON " + table.getSQL();
if (mainIndexColumn >= 0 && mainIndexColumn < indexColumns.length) {
sql += "(" + indexColumns[mainIndexColumn].getSQL() + ")";
}
DbException e = DbException.get(ErrorCode.DUPLICATE_KEY_1, sql);
e.setSource(this);
throw e;
}
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
throw mvTable.convertException(e); throw mvTable.convertException(e);
} }
......
...@@ -1198,16 +1198,6 @@ public class TransactionStore { ...@@ -1198,16 +1198,6 @@ public class TransactionStore {
return get(key, readLogId); return get(key, readLogId);
} }
/**
* Get the most recent value for the given key.
*
* @param key the key
* @return the value or null
*/
public V getLatest(K key) {
return get(key, Long.MAX_VALUE);
}
/** /**
* Whether the map contains the key. * Whether the map contains the key.
* *
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论