allow Byteable instances that have maxSize() determined at runtime

This commit is contained in:
epickrram
2017-07-10 10:27:40 +01:00
parent bd54a98c42
commit ef3e0bbe69
6 changed files with 236 additions and 9 deletions
+1 -1
View File
@@ -47,7 +47,7 @@
<groupId>net.openhft</groupId>
<artifactId>chronicle-bom</artifactId>
<type>pom</type>
<version>1.14.29-SNAPSHOT</version>
<version>1.14.31-SNAPSHOT</version>
<scope>import</scope>
</dependency>
@@ -19,6 +19,7 @@ package net.openhft.chronicle.hash.serialization.impl;
import net.openhft.chronicle.bytes.Byteable;
import net.openhft.chronicle.bytes.BytesMarshallable;
import net.openhft.chronicle.bytes.DynamicallySized;
import net.openhft.chronicle.core.OS;
import net.openhft.chronicle.core.annotation.NotNull;
import net.openhft.chronicle.core.util.ReadResolvable;
@@ -190,8 +191,9 @@ public final class SerializationBuilder<T> implements Cloneable {
}
public long constantSizeBySample(T sampleObject) {
// TODO mark.price this should probably be maxSize()
long constantSize = serializationSize(sampleObject);
if (constantSizeMarshaller()) {
if (constantSizeMarshaller() && !DynamicallySized.class.isAssignableFrom(sampleObject.getClass())) {
long expectedConstantSize = constantSize();
if (constantSize != expectedConstantSize) {
throw new IllegalStateException("Although configuring constant size by sample " +
@@ -820,6 +820,11 @@ public final class ChronicleMapBuilder<K, V> implements
if (maxChunksPerEntry < 1)
throw new IllegalArgumentException("maxChunksPerEntry should be >= 1, " +
maxChunksPerEntry + " given");
if (constantlySizedEntries()) {
throw new IllegalStateException("Sizes of key type: " + keyBuilder.tClass + " and " +
"value type: " + valueBuilder.tClass + " are both constant, " +
"so maxChunksPerEntry shouldn't be specified manually");
}
this.maxChunksPerEntry = maxChunksPerEntry;
return this;
}
@@ -1821,6 +1826,11 @@ public final class ChronicleMapBuilder<K, V> implements
keyBuilder, averageKeySize, averageKey, sampleKey, "Key");
averageValueSize = preMapConstruction(
valueBuilder, averageValueSize, averageValue, sampleValue, "Value");
// TODO mark.price
// System.out.printf(Thread.currentThread().getName() + "|++!! averageValueSize calculated as %.2f%n",
// averageValueSize);
stateChecks();
}
@@ -0,0 +1,216 @@
package net.openhft.chronicle.map;
import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.hash.replication.ReplicableEntry;
import net.openhft.chronicle.map.impl.CompiledReplicatedMapQueryContext;
import org.hamcrest.CoreMatchers;
import org.junit.Ignore;
import org.junit.Test;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import static org.junit.Assert.assertThat;
public class BasicReplicationTest {
@Ignore
@Test
public void shouldReplicate() throws Exception {
final ChronicleMapBuilder<String, String> builder = ChronicleMap.of(String.class, String.class)
.entries(1000).averageKeySize(7).averageValueSize(7);
try (
ReplicatedChronicleMap<String, String, Object> mapOne = createReplicatedMap(builder, asByte(1));
ReplicatedChronicleMap<String, String, Object> mapTwo = createReplicatedMap(builder, asByte(2));
ReplicatedChronicleMap<String, String, Object> mapThree = createReplicatedMap(builder, asByte(3));
) {
final ReplicationEventProcessor<String, String> processorOne =
new ReplicationEventProcessor<>();
processorOne.addDestinationMap(mapOne.acquireModificationIterator(asByte(2)), mapOne, mapTwo);
processorOne.addDestinationMap(mapOne.acquireModificationIterator(asByte(3)), mapOne, mapThree);
final ReplicationEventProcessor<String, String> processorTwo =
new ReplicationEventProcessor<>();
processorTwo.addDestinationMap(mapTwo.acquireModificationIterator(asByte(1)), mapTwo, mapOne);
processorTwo.addDestinationMap(mapTwo.acquireModificationIterator(asByte(3)), mapTwo, mapThree);
final ReplicationEventProcessor<String, String> processorThree =
new ReplicationEventProcessor<>();
processorThree.addDestinationMap(mapThree.acquireModificationIterator(asByte(1)), mapThree, mapOne);
processorThree.addDestinationMap(mapThree.acquireModificationIterator(asByte(2)), mapThree, mapTwo);
final ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(processorOne::processPendingChangesLoop);
executorService.submit(processorTwo::processPendingChangesLoop);
executorService.submit(processorTwo::processPendingChangesLoop);
final Map[] maps = new Map[] {mapOne, mapTwo, mapThree};
final Random random = new Random(0xBAD5EED);
for (int i = 0; i < 50; i++) {
final int mapIndex = random.nextInt(maps.length);
final Map map = maps[mapIndex];
final String key = "key" + random.nextInt(100);
final String value = "val" + random.nextInt(500);
map.put(key, value);
System.out.printf("map %d, put(%s, %s)%n", mapIndex, key, value);
}
LockSupport.parkNanos(TimeUnit.DAYS.toNanos(5L));
executorService.shutdownNow();
for(String key : mapOne.keySet()) {
final String mapOneValue = mapOne.get(key);
final String mapTwoValue = mapTwo.get(key);
final String mapThreeValue = mapThree.get(key);
assertThat(mapOneValue, CoreMatchers.equalTo(mapTwoValue));
assertThat(mapOneValue, CoreMatchers.equalTo(mapThreeValue));
}
}
}
private static byte asByte(final int i) {
return (byte) i;
}
private static final class ReplicationEventProcessor<K, V> {
private final List<IteratorAndDestinationMap<K, V>> destinationMaps = new ArrayList<>();
private final AtomicBoolean queueEmpty = new AtomicBoolean(true);
private ReplicatedChronicleMap<K, V, ?> sourceMap;
void addDestinationMap(final ReplicatedChronicleMap<K, V, ?>.ModificationIterator modificationIterator,
final ReplicatedChronicleMap<K, V, ?> sourceMap,
final ReplicatedChronicleMap<K, V, ?> destinationMap) {
destinationMaps.add(new IteratorAndDestinationMap<K, V>(modificationIterator, sourceMap, destinationMap));
if (this.sourceMap != null && this.sourceMap != sourceMap) {
throw new IllegalArgumentException("All iterators must belong to the same source map");
}
this.sourceMap = sourceMap;
modificationIterator.setModificationNotifier(this::wakeup);
}
void wakeup() {
queueEmpty.set(false);
}
void processPendingChangesLoop() {
while(!Thread.currentThread().isInterrupted()) {
while (queueEmpty.get()) {
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(7));
}
Collections.shuffle(destinationMaps);
for (IteratorAndDestinationMap<K, V> iteratorAndDestinationMap : destinationMaps) {
while (iteratorAndDestinationMap.modificationIterator.hasNext()) {
iteratorAndDestinationMap.modificationIterator.nextEntry(iteratorAndDestinationMap, 17);
}
}
queueEmpty.compareAndSet(false, true);
}
}
}
private static final class IteratorAndDestinationMap<K, V> implements Replica.ModificationIterator.Callback {
private final ReplicatedChronicleMap<K, V, ?>.ModificationIterator modificationIterator;
private final ReplicatedChronicleMap<K, V, ?> sourceMap;
private final ReplicatedChronicleMap<K, V, ?> destinationMap;
private final Bytes<ByteBuffer> buffer = Bytes.elasticByteBuffer();
IteratorAndDestinationMap(final ReplicatedChronicleMap<K, V, ?>.ModificationIterator modificationIterator,
final ReplicatedChronicleMap<K, V, ?> sourceMap,
final ReplicatedChronicleMap<K, V, ?> destinationMap) {
this.modificationIterator = modificationIterator;
this.sourceMap = sourceMap;
this.destinationMap = destinationMap;
}
@Override
public void onEntry(final ReplicableEntry entry, final int chronicleId) {
try (CompiledReplicatedMapQueryContext<K, V, ?> iCtx = sourceMap.mapContext()) {
iCtx.updateLock().lock();
try {
sourceMap.writeExternalEntry(entry, null, buffer, chronicleId);
} finally {
iCtx.updateLock().unlock();
}
} catch(Throwable e) {
e.printStackTrace();
}
final long writeLimit = buffer.writeLimit();
buffer.readPosition(0);
buffer.readLimit(writeLimit);
destinationMap.readExternalEntry(buffer, sourceMap.identifier());
}
@Override
public void onBootstrapTime(final long bootstrapTime, final int chronicleId) {
}
}
private ReplicatedChronicleMap<String, String, Object>
createReplicatedMap(final ChronicleMapBuilder<String, String> builder, final byte replicaId) {
final ChronicleMapBuilderPrivateAPI<String, String> privateBuilder =
new ChronicleMapBuilderPrivateAPI<>(builder);
privateBuilder.replication(replicaId);
final ChronicleMap<String, String> map = builder.create();
return (ReplicatedChronicleMap<String, String, Object>) map;
}
private static final class ReplicationCallback implements Replica.ModificationIterator.Callback {
private final ReplicatedChronicleMap<String, String, Object> sourceMap;
private final ReplicatedChronicleMap<String, String, Object> destinationMap;
ReplicationCallback(final ReplicatedChronicleMap<String, String, Object> sourceMap,
final ReplicatedChronicleMap<String, String, Object> destinationMap) {
this.sourceMap = sourceMap;
this.destinationMap = destinationMap;
}
@Override
public void onEntry(final ReplicableEntry entry, final int chronicleId) {
final boolean changed = entry.isChanged();
final byte originIdentifier = entry.originIdentifier();
final long originTimestamp = entry.originTimestamp();
System.out.printf("onEntry(%s, %d, %d)%n", changed, originIdentifier, originTimestamp);
final boolean identifierCheck = sourceMap.identifierCheck(entry, chronicleId);
final Bytes<ByteBuffer> destination = Bytes.elasticByteBuffer();
sourceMap.writeExternalEntry(entry, null, destination, chronicleId);
System.out.println(identifierCheck);
System.out.println("destination: " + destination);
final long writeLimit = destination.writeLimit();
destination.readPosition(0);
destination.readLimit(writeLimit);
destinationMap.readExternalEntry(destination, sourceMap.identifier());
}
@Override
public void onBootstrapTime(final long bootstrapTime, final int chronicleId) {
System.out.printf("onBootstrapTime(%d, %d)%n", bootstrapTime, chronicleId);
}
}
}
@@ -19,29 +19,28 @@ package net.openhft.lang.values;
import net.openhft.chronicle.bytes.Byteable;
import net.openhft.chronicle.bytes.BytesStore;
import net.openhft.chronicle.bytes.DynamicallySized;
import net.openhft.chronicle.bytes.NativeBytesStore;
import net.openhft.chronicle.values.Copyable;
/**
* Created by peter.lawrey on 23/04/2015.
*/
public class DoubleArray implements Byteable, Copyable<DoubleArray> {
public class DoubleArray implements Byteable, Copyable<DoubleArray>, DynamicallySized {
static boolean HACK = true;
private static int CAPACITY = 0; // assume a 32-bit size.
private static int LENGTH = CAPACITY + 4; // assume a 32-bit size.
private static int BASE = LENGTH + 4;
private final int capacity;
private BytesStore bs;
private long offset;
// creates an empty DoubleArray
public DoubleArray() {
}
public DoubleArray(int capacity) {
bs = NativeBytesStore.nativeStoreWithFixedCapacity(BASE + capacity * 8L);
bs.writeInt(CAPACITY, capacity);
offset = 0;
this.capacity = capacity;
}
@Override
@@ -62,7 +61,7 @@ public class DoubleArray implements Byteable, Copyable<DoubleArray> {
@Override
public long maxSize() {
return BASE + length() * 8;
return BASE + capacity * 8;
}
public int length() {
@@ -67,7 +67,6 @@ public class DoubleArrayTest {
}
@Test
@Ignore("TODO What is HACK???")
public void addToAMap() {
DoubleArray a = new DoubleArray(10);
a.setData(new double[]{1, 2, 3, 4, 5});
@@ -77,6 +76,7 @@ public class DoubleArrayTest {
ChronicleMap<Integer, DoubleArray> proxyMap = ChronicleMap
.of(Integer.class, DoubleArray.class)
.constantValueSizeBySample(a)
.entries(2)
.create();
proxyMap.put(1, a);