Add ability to configure name for all ChronicleMaps and ChronicleSets via ChronicleHashBuilder.name(). Add ChronicleHash.toIdentityString()

This commit is contained in:
leventov
2016-11-12 15:43:27 -06:00
parent e7b2836e2a
commit d76f838adb
15 changed files with 225 additions and 14 deletions
@@ -26,6 +26,8 @@ import org.jetbrains.annotations.NotNull;
import java.io.Closeable;
import java.io.File;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -45,6 +47,34 @@ public interface ChronicleHash<K, E extends HashEntry<K>, SC extends HashSegment
*/
File file();
/**
* Returns the name of this {@code ChronicleHash}, configured by {@link
* ChronicleHashBuilder#name(String)}, or {@code null}, if not configured.
*
* @return the name of this this {@link ChronicleMap} or {@link ChronicleSet}
*/
String name();
/**
* Returns a {@code String}, useful for identifying this {@code ChronicleHash} in debugging,
* logging, and error reporting. {@link #toString()} of concrete {@code ChronicleHash}
* subinterfaces, {@link ChronicleMap} and {@link ChronicleSet}, has to follow {@link
* Map#toString()} and {@link Set#toString()} contracts respectively, making it not always
* useful (or even impossible to use, if this {@code ChronicleHash} contains a lot of entries)
* for the purposes listed above.
*
* <p>This method return a string of the form:<br><br>
* [ChronicleMap|ChronicleSet]{name={@link #name()}, file={@link #file()},
* identityHashCode={@link System#identityHashCode System.identityHashCode(thisChronicleHash)}}
* <br><br>
* This form could be changed in any subsequent Chronicle Map library release (including patch
* release). The user code shouldn't depend on this form.
*
* @return a {@code String}, useful for identifying this {@code ChronicleHash} in debugging,
* logging, and error reporting
*/
String toIdentityString();
/**
* Returns the number of entries in this store.
*
@@ -68,12 +68,32 @@ public interface ChronicleHashBuilder<K, H extends ChronicleHash<K, ?, ?, ?>,
*/
B clone();
/**
* Specify the name which will be given to a ChronicleHash, created by this builder. It is
* recommended to give some distinctive name to each created {@code ChronicleHash}, because this
* name is used when logging errors and warnings inside Chronicle Map library itself, so having
* the concrete {@code ChronicleHash} name in logs may help to debug.
*
* <p>{@code name()} is a JVM-level configuration, it is not stored in the persistence file (or
* the other way to say this: they are not parts of <a
* href="https://github.com/OpenHFT/Chronicle-Map/tree/master/spec">the Chronicle Map data store
* specification</a>) and have to be configured explicitly for each created on-heap {@code
* ChronicleHash} instance, even if it is a view of an existing Chronicle Map data store. On the
* other hand, {@code name()} could be different for different views of the same Chronicle
* Map data store.
*
* @param name the name for a ChronicleHash, created by this builder
* @return this builder back
* @see ChronicleHash#name()
*/
B name(String name);
/**
* Set minimum number of segments in hash containers, constructed by this builder. See
* concurrencyLevel in {@link ConcurrentHashMap}.
*
* @param minSegments the minimum number of segments in containers, constructed by this builder
* @return this builder object back
* @return this builder back
*/
B minSegments(int minSegments);
@@ -24,6 +24,8 @@ import java.util.concurrent.TimeUnit;
public interface ChronicleHashBuilderPrivateAPI<K, RO> {
String name();
SerializationBuilder<K> keyBuilder();
int segmentEntrySpaceInnerOffset();
@@ -22,7 +22,6 @@ import net.openhft.chronicle.bytes.BytesStore;
import net.openhft.chronicle.bytes.MappedBytesStoreFactory;
import net.openhft.chronicle.bytes.NativeBytesStore;
import net.openhft.chronicle.core.Maths;
import net.openhft.chronicle.core.Memory;
import net.openhft.chronicle.core.OS;
import net.openhft.chronicle.hash.*;
import net.openhft.chronicle.hash.impl.stage.hash.ChainingInterface;
@@ -73,7 +72,6 @@ public abstract class VanillaChronicleHash<K,
// --- Start of instance fields ---
/////////////////////////////////////////////////
// Version
private String dataFileVersion;
/////////////////////////////////////////////////
@@ -88,7 +86,6 @@ public abstract class VanillaChronicleHash<K,
public DataAccess<K> keyDataAccess;
/////////////////////////////////////////////////
// Checksum entries
public boolean checksumEntries;
/////////////////////////////////////////////////
@@ -90,7 +90,7 @@ public class TierRecovery {
// the slot cleared at (*) should be clear, if it is dirty, only
// a concurrent modification thread could occupy it
throw new ChronicleHashRecoveryFailedException(
"Concurrent modification of ChronicleMap at " + h.file() +
"Concurrent modification of " + h.toIdentityString() +
" while recovery procedure is in progress");
}
checkDuplicateKeys:
@@ -124,8 +124,8 @@ public class TierRecovery {
} while (insertPos != startInsertPos);
throw new ChronicleHashRecoveryFailedException(
"HashLookup overflow should never occur. " +
"It might also be concurrent access to ChronicleMap at " +
h.file() + " while recovery procedure is in progress");
"It might also be concurrent access to " + h.toIdentityString() +
" while recovery procedure is in progress");
}
hlPos = hl.step(hlPos);
} while (hlPos != 0);
@@ -105,6 +105,23 @@ import static net.openhft.chronicle.map.VanillaChronicleMap.alignAddr;
* #constantKeySizeBySample(Object)}, otherwise {@link #averageKey(Object)} or {@link
* #averageKeySize(double)} method, and accordingly for values.
*
* <p><a name="jvm-configurations"></a>
* There are some JVM-level configurations, which are not stored in the ChronicleMap's persistence
* file (or the other way to say this: they are not parts of <a
* href="https://github.com/OpenHFT/Chronicle-Map/tree/master/spec">the Chronicle Map data store
* specification</a>) and have to be configured explicitly for each created on-heap {@code
* ChronicleMap} instance, even if it is a view of an existing Chronicle Map data store. On the
* other hand, JVM-level configurations could be different for different views of the same Chronicle
* Map data store. The list of JVM-level configurations:
* <ul>
* <li>{@link #name(String)}</li>
* <li>{@link #putReturnsNull(boolean)}</li>
* <li>{@link #removeReturnsNull(boolean)}</li>
* <li>{@link #entryOperations(MapEntryOperations)}</li>
* <li>{@link #mapMethods(MapMethods)}</li>
* <li>{@link #defaultValueProvider(DefaultValueProvider)}</li>
* </ul>
*
* @param <K> key type of the maps, produced by this builder
* @param <V> value type of the maps, produced by this builder
* @see ChronicleHashBuilder
@@ -133,6 +150,9 @@ public final class ChronicleMapBuilder<K, V> implements
private static final ConcurrentHashMap<File, Void> fileLockingControl =
new ConcurrentHashMap<>(128);
private static int MAX_BOOTSTRAPPING_HEADER_SIZE = (int) MemoryUnit.KILOBYTES.toBytes(16);
private String name;
SerializationBuilder<K> keyBuilder;
SerializationBuilder<V> valueBuilder;
K averageKey;
@@ -413,6 +433,20 @@ public final class ChronicleMapBuilder<K, V> implements
return privateAPI;
}
/**
* {@inheritDoc}
* <a href="#jvm-configurations">Read more about JVM-level configurations</a>.
*/
@Override
public ChronicleMapBuilder<K, V> name(String name) {
this.name = name;
return this;
}
String name() {
return this.name;
}
/**
* {@inheritDoc}
*
@@ -812,7 +846,7 @@ public final class ChronicleMapBuilder<K, V> implements
* explicitly for being sure and to compare performance in your case).
*
* @param alignment the new alignment of the maps constructed by this builder
* @return this {@code ChronicleMapOnHeapUpdatableBuilder} back
* @return this builder back
* @throws IllegalStateException if values of maps, created by this builder, couldn't reference
* off-heap memory
*/
@@ -1112,8 +1146,10 @@ public final class ChronicleMapBuilder<K, V> implements
* the data from off-heap memory. A collection hiding remote queries over the network should
* send the value back in addition to that. It's expensive for something you probably don't use.
*
* <p>By default, of cause, {@code ChronicleMap} conforms the general {@code Map} contract and
* returns the previous mapped value on {@code put()} calls.
* <p>This is a <a href="#jvm-configurations">JVM-level configuration</a>.
*
* <p>By default, {@code ChronicleMap} conforms the general {@code Map} contract and returns the
* previous mapped value on {@code put()} calls.
*
* @param putReturnsNull {@code true} if you want {@link ChronicleMap#put(Object, Object)
* ChronicleMap.put()} to not return the value that was replaced but
@@ -1141,8 +1177,10 @@ public final class ChronicleMapBuilder<K, V> implements
* from off-heap memory. A collection hiding remote queries over the network should send
* the value back in addition to that. It's expensive for something you probably don't use.
*
* <p>By default, of cause, {@code ChronicleMap} conforms the general {@code Map} contract and
* returns the mapped value on {@code remove()} calls.
* <p>This is a <a href="#jvm-configurations">JVM-level configuration</a>.
*
* <p>By default, {@code ChronicleMap} conforms the general {@code Map} contract and returns the
* mapped value on {@code remove()} calls.
*
* @param removeReturnsNull {@code true} if you want {@link ChronicleMap#remove(Object)
* ChronicleMap.remove()} to not return the value of the removed entry
@@ -1408,8 +1446,10 @@ public final class ChronicleMapBuilder<K, V> implements
* Specifies the function to obtain a value for the key during {@link ChronicleMap#acquireUsing
* acquireUsing()} calls, if the key is absent in the map, created by this builder.
*
* <p>This is a <a href="#jvm-configurations">JVM-level configuration</a>.
*
* @param defaultValueProvider the strategy to obtain a default value by the absent key
* @return this builder object back
* @return this builder back
*/
public ChronicleMapBuilder<K, V> defaultValueProvider(
@NotNull DefaultValueProvider<K, V> defaultValueProvider) {
@@ -1771,6 +1811,10 @@ public final class ChronicleMapBuilder<K, V> implements
* <p>This affects behaviour of ordinary map.put(), map.remove(), etc. calls, as well as removes
* and replacing values <i>during iterations</i>, <i>remote map calls</i> and
* <i>internal replication operations</i>.
*
* <p>This is a <a href="#jvm-configurations">JVM-level configuration</a>.
*
* @return this builder back
*/
public ChronicleMapBuilder<K, V> entryOperations(MapEntryOperations<K, V, ?> entryOperations) {
Objects.requireNonNull(entryOperations);
@@ -1784,6 +1828,10 @@ public final class ChronicleMapBuilder<K, V> implements
* {@link ChronicleMap#merge}.
*
* <p>This affects behaviour of ordinary map calls, as well as <i>remote calls</i>.
*
* <p>This is a <a href="#jvm-configurations">JVM-level configuration</a>.
*
* @return this builder back
*/
public ChronicleMapBuilder<K, V> mapMethods(MapMethods<K, V, ?> mapMethods) {
Objects.requireNonNull(mapMethods);
@@ -32,6 +32,11 @@ class ChronicleMapBuilderPrivateAPI<K, V>
this.b = b;
}
@Override
public String name() {
return b.name();
}
@Override
public SerializationBuilder<K> keyBuilder() {
return b.keyBuilder();
@@ -75,7 +75,7 @@ class OldDeletedEntriesCleanupThread extends Thread
private long removedCompletely;
OldDeletedEntriesCleanupThread(ReplicatedChronicleMap<?, ?, ?> map) {
super("Cleanup Thread for ReplicatedChronicleMap persisted to " + map.file());
super("Cleanup Thread for " + map.toIdentityString());
this.mapRef = new WeakReference<>(map);
cleanupTimeout = map.cleanupTimeout;
cleanupTimeoutUnit = map.cleanupTimeoutUnit;
@@ -66,6 +66,9 @@ public class VanillaChronicleMap<K, V, R>
public int alignment;
public int worstAlignment;
/////////////////////////////////////////////////
private transient String name;
public transient boolean couldNotDetermineAlignmentBeforeAllocation;
/////////////////////////////////////////////////
@@ -134,6 +137,7 @@ public class VanillaChronicleMap<K, V, R>
}
void initTransientsFromBuilder(ChronicleMapBuilder<K, V> builder) {
name = builder.name();
putReturnsNull = builder.putReturnsNull();
removeReturnsNull = builder.removeReturnsNull();
@@ -239,6 +243,22 @@ public class VanillaChronicleMap<K, V, R>
public String toString() {
return mapToString();
}
@Override
public String name() {
return name;
}
@Override
public String toIdentityString() {
if (chronicleSet != null)
return chronicleSet.toIdentityString();
return "ChronicleMap{" +
"name=" + name() +
", file=" + file() +
", identityHashCode=" + System.identityHashCode(this) +
"}";
}
@Override
public void clear() {
@@ -81,6 +81,12 @@ public final class ChronicleSetBuilder<K>
}
}
@Override
public ChronicleSetBuilder<K> name(String name) {
chronicleMapBuilder.name(name);
return this;
}
@Override
public ChronicleSetBuilder<K> actualSegments(int actualSegments) {
chronicleMapBuilder.actualSegments(actualSegments);
@@ -37,6 +37,11 @@ class ChronicleSetBuilderPrivateAPI<K>
this.mapB = mapB;
}
@Override
public String name() {
return mapB.name();
}
@Override
public SerializationBuilder<K> keyBuilder() {
return mapB.keyBuilder();
@@ -90,6 +90,15 @@ class SetFromMap<E> extends AbstractSet<E> implements ChronicleSet<E> {
return s.toString();
}
@Override
public String toIdentityString() {
return "ChronicleSet{" +
"name=" + name() +
", file=" + file() +
", identityHashCode=" + System.identityHashCode(this) +
"}";
}
public int hashCode() {
return s.hashCode();
}
@@ -179,6 +188,11 @@ class SetFromMap<E> extends AbstractSet<E> implements ChronicleSet<E> {
return m.file();
}
@Override
public String name() {
return m.name();
}
@Override
public void close() {
m.close();