Rob Austin
2020-03-04 12:21:54 +00:00
parent 4cb4ede6ab
commit 932f395561
5 changed files with 118 additions and 34 deletions
+1
View File
@@ -212,6 +212,7 @@
<version>1.5.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
@@ -213,5 +213,36 @@ public interface ChronicleMap<K, V> extends ConcurrentMap<K, V>,
* @return the value Class or UnresolvedType if unknown.
*/
Type valueType();
}
/**
* @return the amount of free space in the map as a percentage.
* When the free space gets low ( around 5% ) the map will automatically expand. The
* number of times it can automatically expand is based on the
* {@code net.openhft.chronicle.map.ChronicleMapBuilder#maxBloatFactor}. If the map
* expands you will see an increase in the available free space. It is not possible to
* expand the chronicle map manually.
*
* see also {@see net.openhft.chronicle.map.ChronicleMap#remainingAutoResizes} as these operations are related.
*/
default short percentageFreeSpace() {
throw new UnsupportedOperationException("todo");
}
/**
* @return the number of times in the future the map can expand its capacity
* ( by expending capacity we mean the number of possible entries that
* Can be stored into the map), the map will expand automatically, however, there is an upper limit to the
* number of times the map can expand.
* This limit is set via the {@code net.openhft.chronicle.map.ChronicleMapBuilder#maxBloatFactor} if
* the {@code remainingResizes} drops to zero,
* then the map is no longer able to resize, if subsequently, the free space
* ( see {@link net.openhft.chronicle.map.ChronicleMap#percentageFreeSpace}) if the map becomes low ( around 5% ),
* the map will not be able to take more entries and will fail with an {@code
* java.lang.IllegalStateException} for production systems it is recommended you periodically monitor this result and the both the
* {@link net.openhft.chronicle.map.ChronicleMap#percentageFreeSpace}.
*/
default int remainingAutoResizes() {
throw new UnsupportedOperationException("todo");
}
}
@@ -204,7 +204,7 @@ public final class ChronicleMapBuilder<K, V> implements
private int maxChunksPerEntry = -1;
private int alignment = UNDEFINED_ALIGNMENT_CONFIG;
private long entries = -1L;
private double maxBloatFactor = 1.0;
double maxBloatFactor = 1.0;
private boolean allowSegmentTiering = true;
private double nonTieredSegmentsPercentile = 0.99999;
private boolean aligned64BitMemoryOperationsAtomic = OS.is64Bit();
@@ -16,6 +16,9 @@
package net.openhft.chronicle.map;
import impl.CompiledMapIterationContext;
import impl.CompiledMapQueryContext;
import net.openhft.chronicle.algo.bitset.ReusableBitSet;
import net.openhft.chronicle.algo.hashing.LongHashFunction;
import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.bytes.BytesStore;
@@ -34,7 +37,9 @@ import net.openhft.chronicle.hash.serialization.DataAccess;
import net.openhft.chronicle.hash.serialization.SizeMarshaller;
import net.openhft.chronicle.hash.serialization.SizedReader;
import net.openhft.chronicle.hash.serialization.impl.SerializationBuilder;
import net.openhft.chronicle.map.impl.*;
import net.openhft.chronicle.map.impl.IterationContext;
import net.openhft.chronicle.map.impl.NullReturnValue;
import net.openhft.chronicle.map.impl.QueryContextInterface;
import net.openhft.chronicle.map.impl.ret.InstanceReturnValue;
import net.openhft.chronicle.set.ChronicleSet;
import net.openhft.chronicle.wire.WireIn;
@@ -51,11 +56,14 @@ import java.util.function.Function;
import static net.openhft.chronicle.map.ChronicleMapBuilder.greatestCommonDivisor;
@SuppressWarnings("JavadocReference")
public class VanillaChronicleMap<K, V, R>
extends VanillaChronicleHash<K, MapEntry<K, V>, MapSegmentContext<K, V, ?>,
ExternalMapQueryContext<K, V, ?>>
implements AbstractChronicleMap<K, V> {
private final double maxBloatFactor;
public SizeMarshaller valueSizeMarshaller;
public SizedReader<V> valueReader;
public DataAccess<V> valueDataAccess;
@@ -98,6 +106,7 @@ public class VanillaChronicleMap<K, V, R>
valueSizeMarshaller = valueBuilder.sizeMarshaller();
valueReader = valueBuilder.reader();
valueDataAccess = valueBuilder.dataAccess();
maxBloatFactor = builder.maxBloatFactor;
constantlySizedEntry = builder.constantlySizedEntries();
@@ -226,6 +235,51 @@ public class VanillaChronicleMap<K, V, R>
return valueClass;
}
private long tiersUsed() {
return globalMutableState().getExtraTiersInUse();
}
private long maxTiers() {
return (long) (maxBloatFactor * actualSegments);
}
@Override
public int remainingAutoResizes() {
return (int) (maxTiers() - tiersUsed());
}
@Override
public short percentageFreeSpace() {
double totalUsed = 0;
double totalSize = 0;
try (IterationContext<K, V, ?> c = iterationContext()) {
for (int segmentIndex = 0; segmentIndex < segments(); segmentIndex++) {
c.initSegmentIndex(segmentIndex);
if (!(c instanceof CompiledMapIterationContext))
continue;
CompiledMapIterationContext c1 = (CompiledMapIterationContext) c;
c1.goToFirstTier();
ReusableBitSet freeList = c1.freeList();
totalUsed += freeList.cardinality();
totalSize += freeList.logicalSize();
while (c1.hasNextTier()) {
c1.nextTier();
ReusableBitSet freeList0 = c1.freeList();
totalUsed += freeList0.cardinality();
totalSize += freeList.logicalSize();
}
}
}
return (short) (100 - (int) (100 * totalUsed / totalSize));
}
@NotNull
@Override
public final Closeable acquireContext(K key, V usingValue) {
@@ -19,6 +19,7 @@ package net.openhft.chronicle.map.jsr166.map;
import net.openhft.chronicle.map.ChronicleMap;
import net.openhft.chronicle.map.ChronicleMapBuilder;
import net.openhft.chronicle.map.jsr166.JSR166TestCase;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
@@ -670,40 +671,37 @@ public class ChronicleMapTest extends JSR166TestCase {
}
}
/**
* A deserialized map equals original
*/
/* @Test public void testSerialization() {
Map x = map5();
Map y = serialClone(x);
@Test
public void testPercentageComplete() {
assertNotSame(x, y);
assertEquals(x.size(), y.size());
assertEquals(x, y);
assertEquals(y, x);
}*/
try (ChronicleMap<Integer, Integer> map = ChronicleMap
.of(Integer.class, Integer.class)
.entries(1600)
.actualSegments(3)
.maxBloatFactor(2)
.create()) {
long remainingAutoResizes = 0;
short percentageFreeSpace = 0;
try {
for (int i = 0; ; i++) {
map.put(i, 0);
remainingAutoResizes = map.remainingAutoResizes();
percentageFreeSpace = map.percentageFreeSpace();
}
} catch (IllegalStateException e) {
if (e.getMessage().contains("Attempt to allocate")) {
Assert.assertEquals(0, (int) remainingAutoResizes);
Assert.assertTrue(percentageFreeSpace < 6);
return;
}
Assert.fail();
}
/**
* TODO : SetValue of an EntrySet entry sets value in the map.
*/
/*@Test public void testSetValueWriteThrough() {
// Adapted from a bug report by Eric Zoerner
ChronicleMap map = newShmIntString(2, 5.0f, 1);
assertTrue(map.isEmpty());
for (int i = 0; i < 20; i++)
map.put(new Integer(i), new Integer(i));
assertFalse(map.isEmpty());
Map.Entry entry1 = (Map.Entry) map.entrySet().iterator().next();
// Unless it happens to be first (in which case remainder of
// test is skipped), remove a possibly-colliding key from map
// which, under some implementations, may cause entry1 to be
// cloned in map
if (!entry1.getKey().equals(new Integer(16))) {
map.remove(new Integer(16));
entry1.setValue("XYZ");
assertTrue(map.containsValue("XYZ")); // fails if write-through broken
}
}*/
}
}