HCOLL-81 StatelessClient : Add a visitor pattern to Chronicle Map

This commit is contained in:
Rob Austin
2014-11-13 18:55:15 +00:00
parent 316951efba
commit bfc2dcac25
4 changed files with 210 additions and 77 deletions
@@ -955,67 +955,77 @@ final class ReplicatedChronicleMap<K, KI, MKI extends MetaBytesInterop<K, KI>,
return keySize == keySizeMarshaller.readSize(entry) && entry.startsWith(keyBytes);
}
@Override
V putWithoutLock(ThreadLocalCopies copies, MKI metaKeyInterop, KI keyInterop, K key, V value, long hash2, boolean replaceIfPresent) {
return putWithoutLock(copies, metaKeyInterop, keyInterop, key, value, hash2, replaceIfPresent,
localIdentifier, timeProvider.currentTimeMillis());
}
V put(ThreadLocalCopies copies, MKI metaKeyWriter, KI keyWriter,
K key, V value, long hash2, boolean replaceIfPresent,
final byte identifier, final long timestamp) {
writeLock();
try {
MultiMap hashLookup = hashLookupLiveAndDeleted;
long keySize = metaKeyWriter.size(keyWriter, key);
hashLookup.startSearch(hash2);
for (long pos; (pos = hashLookup.nextPos()) >= 0L; ) {
long offset = offsetFromPos(pos);
MultiStoreBytes entry = entry(offset);
if (!keyEquals(keyWriter, metaKeyWriter, key, keySize, entry))
continue;
// key is found
entry.skip(keySize);
final long timeStampPos = entry.positionAddr();
if (shouldIgnore(entry, timestamp, identifier))
return null;
boolean wasDeleted = entry.readBoolean();
// if wasDeleted==true then we even if replaceIfPresent==false we can treat it
// the same way as replaceIfPresent true
if (replaceIfPresent || wasDeleted) {
entry.positionAddr(timeStampPos);
entry.writeLong(timestamp);
entry.writeByte(identifier);
// deleted flag
entry.writeBoolean(false);
final V prevValue = replaceValueOnPut(copies, key, value, entry, pos,
offset, !wasDeleted && !putReturnsNull, hashLookup);
if (wasDeleted) {
// remove() would have got rid of this so we have to add it back in
hashLookupLiveOnly.put(hash2, pos);
incrementSize();
return null;
} else {
return prevValue;
}
} else {
// we wont replaceIfPresent and the entry is not deleted
return putReturnsNull ? null : readValue(copies, entry, null);
}
}
// key is not found
long offset = putEntry(copies, metaKeyWriter, keyWriter, key, keySize, hash2, value,
identifier, timestamp, hashLookup);
incrementSize();
notifyPut(offset, true, key, value, null, posFromOffset(offset));
return null;
return putWithoutLock(copies, metaKeyWriter, keyWriter, key, value, hash2, replaceIfPresent, identifier, timestamp);
} finally {
writeUnlock();
}
}
private V putWithoutLock(ThreadLocalCopies copies, MKI metaKeyWriter, KI keyWriter, K key, V value, long hash2, boolean replaceIfPresent, byte identifier, long timestamp) {
MultiMap hashLookup = hashLookupLiveAndDeleted;
long keySize = metaKeyWriter.size(keyWriter, key);
hashLookup.startSearch(hash2);
for (long pos; (pos = hashLookup.nextPos()) >= 0L; ) {
long offset = offsetFromPos(pos);
MultiStoreBytes entry = entry(offset);
if (!keyEquals(keyWriter, metaKeyWriter, key, keySize, entry))
continue;
// key is found
entry.skip(keySize);
final long timeStampPos = entry.positionAddr();
if (shouldIgnore(entry, timestamp, identifier))
return null;
boolean wasDeleted = entry.readBoolean();
// if wasDeleted==true then we even if replaceIfPresent==false we can treat it
// the same way as replaceIfPresent true
if (replaceIfPresent || wasDeleted) {
entry.positionAddr(timeStampPos);
entry.writeLong(timestamp);
entry.writeByte(identifier);
// deleted flag
entry.writeBoolean(false);
final V prevValue = replaceValueOnPut(copies, key, value, entry, pos,
offset, !wasDeleted && !putReturnsNull, hashLookup);
if (wasDeleted) {
// remove() would have got rid of this so we have to add it back in
hashLookupLiveOnly.put(hash2, pos);
incrementSize();
return null;
} else {
return prevValue;
}
} else {
// we wont replaceIfPresent and the entry is not deleted
return putReturnsNull ? null : readValue(copies, entry, null);
}
}
// key is not found
long offset = putEntry(copies, metaKeyWriter, keyWriter, key, keySize, hash2, value,
identifier, timestamp, hashLookup);
incrementSize();
notifyPut(offset, true, key, value, null, posFromOffset(offset));
return null;
}
/**
* Used only with replication, its sometimes possible to receive an old ( or stale update ) from a
@@ -35,6 +35,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.SocketChannel;
import java.util.*;
import java.util.function.Function;
import static java.nio.ByteBuffer.allocateDirect;
import static net.openhft.chronicle.map.AbstractChannelReplicator.SIZE_OF_SIZE;
@@ -90,13 +91,15 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
TO_STRING,
PUT_ALL,
PUT_ALL_WITHOUT_ACC,
HASH_CODE
HASH_CODE,
MAP_FOR_KEY
}
private long transactionID;
StatelessChronicleMap(final StatelessMapConfig builder,
final AbstractChronicleMapBuilder chronicleMapBuilder) throws IOException {
final AbstractChronicleMapBuilder chronicleMapBuilder) throws
IOException {
this.builder = builder;
this.keyValueSerializer = new KeyValueSerializer<K, V>(chronicleMapBuilder.keyBuilder,
chronicleMapBuilder.valueBuilder);
@@ -105,8 +108,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
this.maxEntrySize = chronicleMapBuilder.entrySize(true);
this.vClass = chronicleMapBuilder.valueBuilder.eClass;
this.kClass = chronicleMapBuilder.keyBuilder.eClass;
// this.putReturnsNull = putReturnsNull;
// this.removeReturnsNull = removeReturnsNull;
attemptConnect(builder.remoteAddress());
buffer = allocateDirect(maxEntrySize).order(ByteOrder.nativeOrder());
@@ -136,6 +138,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
result = AbstractChannelReplicator.openSocketChannel(closeables);
result.connect(builder.remoteAddress());
result.socket().setTcpNoDelay(true);
result.socket().setReuseAddress(true);
doHandShaking(result);
break;
@@ -451,9 +454,44 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
}
public synchronized <R> R mapForKey(K key, @NotNull Function<V, R> function) {
final ThreadLocalCopies local = keyValueSerializer.threadLocalCopies();
final long sizeLocation = writeEventAnSkip(MAP_FOR_KEY);
writeKey(key, local);
writeObject(function);
final Bytes reader = blockingFetch(sizeLocation);
return (R) keyValueSerializer.readObject(reader);
}
private <R> void writeObject(@NotNull Function<V, R> function) {
for (; ; ) {
try {
bytes.writeObject(function);
return;
} catch (IllegalStateException e) {
Throwable cause = e.getCause();
if (cause instanceof IOException && cause.getMessage().contains("Not enough available space")) {
LOG.debug("resizing buffer");
resizeBuffer(buffer.capacity() + 1024);
} else
throw e;
}
}
}
public synchronized void putAll(Map<? extends K, ? extends V> map) {
final long sizeLocation = (putReturnsNull) ? writeEvent(PUT_ALL_WITHOUT_ACC) : writeEventAnSkip(PUT_ALL);
final long sizeLocation = putReturnsNull ? writeEvent(PUT_ALL_WITHOUT_ACC) : writeEventAnSkip(PUT_ALL);
writeEntries(map);
@@ -560,8 +598,6 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
// we will back the size estimate on what we have so far
final double percentageComplete = (double) numberOfEntriesReadSoFar / (double) numberOfEntries;
return (long) ((double) bytes.position() / percentageComplete);
}
@@ -571,30 +607,38 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
assert size < Integer.MAX_VALUE;
final ByteBuffer result = ByteBuffer.allocateDirect((int) size).order(ByteOrder.nativeOrder());
final ByteBuffer result = ByteBuffer.allocate((int) size).order(ByteOrder.nativeOrder());
final int bufferPosition = buffer.position();
/* final int bufferPosition = buffer.position();
final int bufferLimit = buffer.limit();
*/
final long bytesPosition = bytes.position();
bytes = new ByteBufferBytes(result.slice());
buffer.position(0);
buffer.limit((int) bytesPosition);
for (int i = 0; i < bytesPosition; i++) {
result.put(buffer.get());
int numberOfLongs = (int) bytesPosition / 8;
// chunk in longs first
for (int i = 0; i < numberOfLongs; i++) {
bytes.writeLong(buffer.getLong());
}
for (int i = numberOfLongs * 8; i < bytesPosition; i++) {
bytes.writeByte(buffer.get());
}
buffer = result;
buffer.limit(bufferLimit);
/* buffer.limit(bufferLimit);
buffer.position(bufferPosition);
*/
assert buffer.capacity() == bytes.capacity();
bytes.limit(bytes.capacity());
/* bytes.limit(bytes.capacity());
bytes.position(bytesPosition);
*/
assert buffer.capacity() == size;
assert buffer.capacity() == bytes.capacity();
assert bytes.limit() == bytes.capacity();
@@ -731,7 +775,6 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
buffer.clear();
bytes.clear();
//bytes.limit(buffer.capacity());
bytes.write((byte) event.ordinal());
long sizeLocation = markSizeLocation();
@@ -807,6 +850,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
public final String toString() {
return getKey() + "=" + getValue();
}
}
private Bytes blockingFetch(long sizeLocation) {
@@ -954,7 +998,6 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
bytes.writeInt((int) size - SIZE_OF_SIZE);
bytes.writeLong(transactionId);
bytes.position(pos);
}
@@ -978,7 +1021,6 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
resizeBuffer(bytes.capacity() + maxEntrySize);
}
}
@@ -988,7 +1030,6 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
} catch (IllegalArgumentException e) {
resizeBuffer(bytes.capacity() + maxEntrySize);
}
}
private V readKey(final long sizeLocation) {
@@ -1005,7 +1046,6 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
} catch (IllegalArgumentException e) {
resizeBuffer(bytes.capacity() + maxEntrySize);
}
}
private void writeValue(V value, ThreadLocalCopies local) {
@@ -1014,7 +1054,6 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
} catch (IllegalArgumentException e) {
resizeBuffer(bytes.capacity() + maxEntrySize);
}
}
}
@@ -37,6 +37,8 @@ import java.nio.ByteOrder;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import static java.nio.channels.SelectionKey.*;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -1253,7 +1255,7 @@ class StatelessServerConnector<K, V> {
final StatelessChronicleMap.EventId event = StatelessChronicleMap.EventId.values()[eventId];
// these methods don't return a result
switch (event) {
case KEY_SET:
@@ -1268,7 +1270,6 @@ class StatelessServerConnector<K, V> {
case PUT_WITHOUT_ACC:
return put(reader);
case PUT_ALL_WITHOUT_ACC:
return putAll(reader);
@@ -1279,6 +1280,9 @@ class StatelessServerConnector<K, V> {
final long sizeLocation = reflectTransactionId(reader, writer);
// these methods return a result
switch (event) {
case LONG_SIZE:
@@ -1326,6 +1330,8 @@ class StatelessServerConnector<K, V> {
case HASH_CODE:
return hashCode(reader, writer, sizeLocation);
case MAP_FOR_KEY:
return mapForKey(reader, writer, sizeLocation);
default:
throw new IllegalStateException("unsupported event=" + event);
@@ -1334,6 +1340,38 @@ class StatelessServerConnector<K, V> {
}
// an object poll of 1 using an atomic reference
private AtomicReference<V> using = new AtomicReference<V>();
public Work mapForKey(ByteBufferBytes reader, Bytes writer, long sizeLocation) {
final ThreadLocalCopies local = keyValueSerializer.threadLocalCopies();
final K key = readKey(reader, local);
V value = using.getAndSet(null);
if (value == null) {
// a simple way to create a value
value = map.getUsing(key, null);
}
final Function<V, ?> o = (Function<V, ?>) reader.readObject();
try (WriteContext<K, V> wc = map.acquireUsingLocked(key, value)) {
Object result = o.apply(value);
keyValueSerializer.writeObject(result, writer);
} catch (RuntimeException e) {
LOG.info("", e);
return sendException(writer, sizeLocation, e);
} finally {
using.set(value);
}
writeSizeAndFlags(sizeLocation, false, writer);
return null;
}
private Work removeWithValue(Bytes reader, Bytes writer, final long sizeLocation) {
if (MAP_SUPPORTS_BYTES) {
@@ -1916,6 +1954,11 @@ class KeyValueSerializer<K, V> {
return valueSerializer.readMarshallable(reader, local);
}
<O> O readObject(Bytes reader) {
if (nullCheck(reader)) return null;
return (O) reader.readObject();
}
private boolean nullCheck(Bytes in) {
return in.readBoolean();
@@ -1976,6 +2019,16 @@ class KeyValueSerializer<K, V> {
}
void writeObject(Object value, Bytes writer) {
assert writer.limit() == writer.capacity();
writer.writeBoolean(value == null);
if (value != null) {
writer.writeObject(value);
}
}
public ThreadLocalCopies threadLocalCopies() {
return valueSerializer.threadLocalCopies();
}