HCOLL-167 added support for putAll()

This commit is contained in:
Rob Austin
2014-10-23 14:52:38 +01:00
parent f04cdd0d9f
commit 60b23535d1
3 changed files with 136 additions and 55 deletions
@@ -64,8 +64,8 @@ import static net.openhft.chronicle.map.Objects.builderEquals;
*
* <p>Use static {@link #of(Class, Class)} method to obtain a {@code ChronicleMapBuilder} instance.
*
* <p>{@code ChronicleMapBuilder} is mutable, see a note in {@link net.openhft.chronicle.common.ChronicleHashBuilder} interface
* documentation.
* <p>{@code ChronicleMapBuilder} is mutable, see a note in {@link net.openhft.chronicle.common.ChronicleHashBuilder}
* interface documentation.
*
* <p>Later in this documentation, "ChronicleMap" means "ChronicleMaps, created by {@code
* ChronicleMapBuilder}", unless specified different, because theoretically someone might provide
@@ -819,8 +819,7 @@ public class ChronicleMapBuilder<K, V> implements Cloneable,
if (statelessBuilder != null)
return createStatelessMap(statelessBuilder);
return (file != null) ? createWithFile(file) : createWithoutFile();
return (file != null) ? createWithFile(file) : createWithoutFile();
}
private ChronicleMap<K, V> createWithoutFile() throws IOException {
@@ -837,7 +836,7 @@ public class ChronicleMapBuilder<K, V> implements Cloneable,
final KeyValueSerializer<K, V> keyValueSerializer
= new KeyValueSerializer<K, V>(keyBuilder, valueBuilder);
return new StatelessChronicleMap<K, V>(keyValueSerializer, statelessBuilder);
return new StatelessChronicleMap<K, V>(keyValueSerializer, statelessBuilder, entrySize);
}
private VanillaChronicleMap<K, ?, ?, V, ?, ?> newMap() throws IOException {
@@ -32,17 +32,16 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
private final byte[] connectionByte = new byte[1];
private final ByteBuffer connectionOutBuffer = ByteBuffer.wrap(connectionByte);
private final ByteBuffer outBuffer = allocateDirect(1024);
private final ByteBufferBytes out = new ByteBufferBytes(outBuffer.slice());
// if you want you could change this later so that "in" has its own buffer.
private ByteBufferBytes in = out;
private ByteBuffer buffer;
private ByteBufferBytes bytes;
private final KeyValueSerializer<K, V> keyValueSerializer;
private volatile SocketChannel clientChannel;
private CloseablesManager closeables;
private final StatelessBuilder builder;
private int maxEntrySize;
private int headroom = 1024;
static enum EventId {
HEARTBEAT,
@@ -61,16 +60,23 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
REPLACE_WITH_OLD_AND_NEW_VALUE,
PUT_IF_ABSENT,
REMOVE_WITH_VALUE,
TO_STRING
TO_STRING,
PUT_ALL
}
private long transactionID;
StatelessChronicleMap(final KeyValueSerializer<K, V> keyValueSerializer,
final StatelessBuilder builder) throws IOException {
final StatelessBuilder builder,
int maxEntrySize) throws IOException {
this.keyValueSerializer = keyValueSerializer;
this.builder = builder;
this.maxEntrySize = maxEntrySize;
attemptConnect(builder.remoteAddress());
headroom = 1024 + maxEntrySize;
buffer = allocateDirect(headroom);
bytes = new ByteBufferBytes(buffer.slice());
}
@@ -339,16 +345,85 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
public synchronized void putAll(Map<? extends K, ? extends V> map) {
for (Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
put(e.getKey(), e.getValue());
final long sizeLocation = writeEvent(PUT_ALL);
final int numberOfEntries = map.size();
int numberOfEntriesReadSoFar = 0;
bytes.writeStopBit(numberOfEntries);
for (final Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
numberOfEntriesReadSoFar++;
// putAll if a bit more complicated than the others
// as the volume of data could cause us to have to resize our buffers
resizeIfRequired(numberOfEntries, numberOfEntriesReadSoFar);
long start = bytes.position();
writeKey(e.getKey());
writeValue(e.getValue());
int len = (int) (bytes.position() - start);
if (len > maxEntrySize)
maxEntrySize = len;
}
blockingFetch(sizeLocation);
}
private void resizeIfRequired(int numberOfEntries, int numberOfEntriesReadSoFar) {
if (bytes.remaining() < maxEntrySize) {
long estimatedRequiredSize = estimateSize(numberOfEntries, numberOfEntriesReadSoFar);
resizeBuffer(estimatedRequiredSize + headroom);
}
}
/**
* estimates the size based on what been completed so far
*
* @param numberOfEntries
* @param numberOfEntriesReadSoFar
*/
private long estimateSize(int numberOfEntries, int numberOfEntriesReadSoFar) {
// we will back the size estimate on what we have so far
double percentageComplete = numberOfEntries / numberOfEntriesReadSoFar;
return (long) ((double) bytes.position() / percentageComplete);
}
void resizeBuffer(long size) {
if (size < buffer.capacity())
throw new IllegalStateException("it not possible to resize the buffer smaller");
ByteBuffer result = ByteBuffer.allocateDirect((int) size);
buffer.clear();
int bufferPosition = buffer.position();
int bufferLimit = buffer.limit();
long bytesPosition = bytes.position();
long bytesLimit = bytes.limit();
buffer.position(0);
for (int i = 0; i < bufferLimit; i++) {
result.put(buffer.get());
}
buffer.limit(bufferLimit);
buffer.position(bufferPosition);
bytes = new ByteBufferBytes(result.slice());
bytes.limit(bytesLimit);
bytes.position(bytesPosition);
}
/* private void writeEntries(Map<? extends K, ? extends V> map) {
final HashMap<K, V> safeCopy = new HashMap<K, V>(map);
out.writeStopBit(safeCopy.size());
bytes.writeStopBit(safeCopy.size());
final Set<Entry> entries = (Set) safeCopy.entrySet();
@@ -419,12 +494,10 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
private long writeEvent(EventId event) {
in.clear();
out.clear();
bytes.clear();
buffer.clear();
outBuffer.clear();
out.write((byte) event.ordinal());
bytes.write((byte) event.ordinal());
long sizeLocation = markSizeLocation();
return sizeLocation;
}
@@ -515,30 +588,30 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
writeSizeAt(sizeLocation);
// send out all the bytes
send(out, timeoutTime);
send(bytes, timeoutTime);
in.clear();
in.buffer().clear();
bytes.clear();
bytes.buffer().clear();
// the number of bytes in the response
int size = receive(2, timeoutTime).readUnsignedShort();
if (in.capacity() < size)
in = new ByteBufferBytes(allocateDirect(size));
if (bytes.capacity() < size)
bytes = new ByteBufferBytes(allocateDirect(size));
receive(size, timeoutTime);
boolean isException = in.readBoolean();
long inTransactionId = in.readLong();
boolean isException = bytes.readBoolean();
long inTransactionId = bytes.readLong();
if (inTransactionId != transactionId)
throw new IllegalStateException("the received transaction-id=" + inTransactionId +
", does not match the expected transaction-id=" + transactionId);
if (isException)
throw (RuntimeException) in.readObject();
throw (RuntimeException) bytes.readObject();
return in;
return bytes;
} catch (java.nio.channels.ClosedChannelException e) {
checkTimeout(timeoutTime);
clientChannel = lazyConnect(builder.timeoutMs(), builder.remoteAddress());
@@ -550,31 +623,31 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
}
private void write(long transactionId) {
out.writeLong(transactionId);
bytes.writeLong(transactionId);
}
private ByteBufferBytes receive(int requiredNumberOfBytes, long timeoutTime) throws IOException {
while (in.buffer().position() < requiredNumberOfBytes) {
clientChannel.read(in.buffer());
while (bytes.buffer().position() < requiredNumberOfBytes) {
clientChannel.read(bytes.buffer());
checkTimeout(timeoutTime);
}
in.limit(in.buffer().position());
return in;
bytes.limit(bytes.buffer().position());
return bytes;
}
private void send(final ByteBufferBytes out, long timeoutTime) throws IOException {
outBuffer.limit((int) out.position());
outBuffer.position(0);
buffer.limit((int) out.position());
buffer.position(0);
while (outBuffer.remaining() > 0) {
clientChannel.write(outBuffer);
while (buffer.remaining() > 0) {
clientChannel.write(buffer);
checkTimeout(timeoutTime);
}
out.clear();
outBuffer.clear();
buffer.clear();
}
@@ -584,18 +657,18 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
}
private void writeSizeAt(long locationOfSize) {
long size = out.position() - locationOfSize;
out.writeUnsignedShort(locationOfSize, (int) size - 2);
long size = bytes.position() - locationOfSize;
bytes.writeUnsignedShort(locationOfSize, (int) size - 2);
}
private long markSizeLocation() {
long position = out.position();
out.skip(2);
long position = bytes.position();
bytes.skip(2);
return position;
}
private void writeKey(K key) {
keyValueSerializer.writeKey(key, out);
keyValueSerializer.writeKey(key, bytes);
}
private V readKey(final long sizeLocation) {
@@ -603,7 +676,7 @@ class StatelessChronicleMap<K, V> implements ChronicleMap<K, V>, Closeable {
}
private void writeValue(V value) {
keyValueSerializer.writeValue(value, out);
keyValueSerializer.writeValue(value, bytes);
}
private Set<K> readKeySet(Bytes in) {
@@ -1036,17 +1036,18 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
void resizeBuffer(long size) {
if (size < in.capacity())
throw new IllegalStateException("it not possible to resize the buffer smaller");
ByteBuffer buffer = ByteBuffer.allocateDirect((int) size);
final ByteBuffer buffer = ByteBuffer.allocateDirect((int) size);
buffer.clear();
long outPosition = out.position();
long outLimit = out.limit();
final long outPosition = out.position();
final long outLimit = out.limit();
int inPosition = in.position();
int inLimit = in.limit();
final int inPosition = in.position();
final int inLimit = in.limit();
in.position(0);
@@ -1054,12 +1055,15 @@ class TcpReplicator extends AbstractChannelReplicator implements Closeable {
buffer.put(in.get());
}
buffer.position(inPosition);
buffer.limit(inLimit);
buffer.position(inPosition);
in = buffer;
out = new ByteBufferBytes(in.slice());
out.position(outPosition);
out.limit(outLimit);
out.position(outPosition);
}
/**
@@ -1293,6 +1297,10 @@ class StatelessServerConnector<K, V> {
case TO_STRING:
return toString(in, out);
case PUT_ALL:
return putAll(in, out);
default:
throw new IllegalStateException("unsupported event=" + event);
@@ -1468,6 +1476,7 @@ class StatelessServerConnector<K, V> {
private Work putAll(Bytes in, Bytes out) {
final Map<K, V> m = readEntries(in);
final long sizeLocation = reflectTransactionId(in, out);
try {
map.putAll(m);
} catch (RuntimeException e) {
@@ -1674,16 +1683,15 @@ class StatelessServerConnector<K, V> {
private Map<K, V> readEntries(Bytes in) {
final long size = in.readStopBit();
final long numberOfEntries = in.readStopBit();
final HashMap<K, V> result = new HashMap<K, V>();
for (long i = 0; i < size; i++) {
for (long i = 0; i < numberOfEntries; i++) {
result.put(readKey(in), readValue(in));
}
return result;
}
}
class KeyValueSerializer<K, V> {
@@ -1728,6 +1736,7 @@ class KeyValueSerializer<K, V> {
valueSerializer.writeMarshallable(value, out);
}
}