Add ChronicleHashBuilder.createOrRecoverPersistedTo(file, sameLibraryVersion) method and emphasize in docs, that the boolean flag in the 'recoverPersistedTo' family of methods couldn't be true, if the version of the library, with which the Chronicle Map persisted to the given file was created, could be different from the present version of the Chronicle Map library

This commit is contained in:
leventov
2017-02-19 22:02:38 -06:00
parent 59a2cd8d80
commit b9893ffab7
6 changed files with 101 additions and 62 deletions
@@ -533,7 +533,7 @@ public interface ChronicleHashBuilder<K, H extends ChronicleHash<K, ?, ?, ?>,
* @see ChronicleHash#file()
* @see ChronicleHash#close()
* @see #create()
* @see #createOrRecoverPersistedTo(File)
* @see #createOrRecoverPersistedTo(File, boolean)
* @see #recoverPersistedTo(File, boolean)
*/
H createPersistedTo(File file) throws IOException;
@@ -541,9 +541,14 @@ public interface ChronicleHashBuilder<K, H extends ChronicleHash<K, ?, ?, ?>,
/**
* Recovers and opens the hash container, persisted to the specified file, or creates a new one
* from this builder, if the file doesn't exist yet, and maps its off-heap memory to the file.
* In other words, this methods behaves like {@link #createPersistedTo(File)}, if the given
* file doesn't exist, and {@link #recoverPersistedTo(File, boolean)
* recoverPersistedTo(file, true)}, if the file exists.
* This method is equivalent to {@link #createOrRecoverPersistedTo(File, boolean)
* createOrRecoverPersistedTo(file, true)}.
*
* <p><b>This method couldn't be used, if the Chronicle Map was created using an older version
* of the Chronicle Map library, and then {@code createOrRecoverPersistedTo()} is called with
* a newer version of the Chronicle Map library.</b> In this case, {@link
* #createOrRecoverPersistedTo(File, boolean) createOrRecoverPersistedTo(file, false)} should
* be used.
*
* @param file the persistence file for existing of future hash container
* @return a {@code ChronicleHash} instance, mapped to the given instance
@@ -553,10 +558,35 @@ public interface ChronicleHashBuilder<K, H extends ChronicleHash<K, ?, ?, ?>,
* calling this procedure.
* @throws ChronicleHashRecoveryFailedException if recovery is impossible
* @see #createPersistedTo(File)
* @see #createOrRecoverPersistedTo(File, boolean)
* @see #recoverPersistedTo(File, boolean)
*/
H createOrRecoverPersistedTo(File file) throws IOException;
/**
* Recovers and opens the hash container, persisted to the specified file, or creates a new one
* from this builder, if the file doesn't exist yet, and maps its off-heap memory to the file.
* In other words, this methods behaves like {@link #createPersistedTo(File)}, if the given
* file doesn't exist, and {@link #recoverPersistedTo(File, boolean)
* recoverPersistedTo(file, sameLibraryVersion)}, if the file exists.
*
* @param file the persistence file for existing of future hash container
* @param sameLibraryVersion if this builder is configured with the same configurations, as the
* builder, which created the Chronicle Map in the given file initially, and <b>the same version
* of the Chronicle Map library was used</b>. In this case, the header of the file is overridden
* (with presumably the same configurations), protecting from {@link
* ChronicleHashRecoveryFailedException}, if the header is corrupted.
* @return a {@code ChronicleHash} instance, mapped to the given instance
* @throws IOException if any IO error occurs on reading data from the file, or related to
* off-heap memory allocation or file mapping, or establishing replication connections. Probably
* the file is corrupted on OS level, and should be recovered on that level first, before
* calling this procedure.
* @throws ChronicleHashRecoveryFailedException if recovery is impossible
* @see #createPersistedTo(File)
* @see #recoverPersistedTo(File, boolean)
*/
H createOrRecoverPersistedTo(File file, boolean sameLibraryVersion) throws IOException;
/**
* <i>Recovers</i> and opens the hash container, persisted to the specified file. This method
* should be used to open a persisted Chronicle Map after an accessor process crash (that might
@@ -567,7 +597,8 @@ public interface ChronicleHashBuilder<K, H extends ChronicleHash<K, ?, ?, ?>,
* which were never put into the Chronicle Hash).
*
* <p>This method, unlike {@link #createPersistedTo(File)} or {@link
* #createOrRecoverPersistedTo(File)} methods, expects that the mapping file already exists.
* #createOrRecoverPersistedTo(File, boolean)} methods, expects that the mapping file already
* exists.
*
* "Recovery" of the hash container is changing the memory of the data structure so that after
* the recovery the hash container is in some correct state: with "clean" locks, coherent
@@ -583,21 +614,19 @@ public interface ChronicleHashBuilder<K, H extends ChronicleHash<K, ?, ?, ?>,
*
* <p>It is strongly recommended to configure this builder with the same configurations, as
* the builder, that created the given file for the first time, and pass {@code true} as the
* {@code sameBuilderConfig} argument (another requirement is running the same version of the
* Chronicle Map library). Otherwise, if the header of the given persisted Chronicle Hash file
* is corrupted, this method is likely to be unable to recover and throw {@link
* ChronicleHashRecoveryFailedException}, or even worse, to corrupt the file further.
* Fortunately, the header should never be corrupted on an "ordinary" process crash/termination
* or power loss, only on direct file corruption.
* {@code sameBuilderConfigAndLibraryVersion} argument <b>(another requirement is running
* the same version of the Chronicle Map library)</b>. Otherwise, if the header of the given
* persisted Chronicle Hash file is corrupted, this method is likely to be unable to recover and
* throw {@link ChronicleHashRecoveryFailedException}, or even worse, to corrupt the file
* further. Fortunately, the header should never be corrupted on an "ordinary" process
* crash/termination or power loss, only on direct file corruption.
*
* @param file a hash container was mapped to the given file
* @param sameBuilderConfig if this builder is configured with the same configurations, as the
* builder, which created the file (the persisted Chronicle Hash
* instance) for the first time, and with the same version of the
* Chronicle Map library. In this case, the header of the file is
* overridden (with presumably the same configurations), protecting
* from {@link ChronicleHashRecoveryFailedException}, if the header is
* corrupted.
* @param sameBuilderConfigAndLibraryVersion if this builder is configured with the same
* configurations, as the builder, which created the file (the persisted Chronicle Hash
* instance) for the first time, and <b>with the same version of the Chronicle Map library</b>.
* In this case, the header of the file is overridden (with presumably the same configurations),
* protecting from {@link ChronicleHashRecoveryFailedException}, if the header is corrupted.
* @return a recovered Chronicle Hash instance, mapped to the given file
* @throws FileNotFoundException if the file doesn't exist
* @throws IOException if any IO error occurs on reading data from the file, or related to
@@ -605,10 +634,10 @@ public interface ChronicleHashBuilder<K, H extends ChronicleHash<K, ?, ?, ?>,
* the file is corrupted on OS level, and should be recovered on that level first, before
* calling this procedure.
* @throws ChronicleHashRecoveryFailedException if recovery is impossible
* @see #createOrRecoverPersistedTo(File)
* @see #createOrRecoverPersistedTo(File, boolean)
* @see #createPersistedTo(File)
*/
H recoverPersistedTo(File file, boolean sameBuilderConfig) throws IOException;
H recoverPersistedTo(File file, boolean sameBuilderConfigAndLibraryVersion) throws IOException;
/**
* @deprecated don't use private API in the client code
@@ -21,7 +21,7 @@ import java.io.File;
/**
* This exception is thrown, when a Chronicle Hash recovery using {@link
* ChronicleHashBuilder#recoverPersistedTo(File, boolean)} method is impossible, for example,
* ChronicleHashBuilder#recoverPersistedTo(File, boolean)} method is impossible, for example,
* if the persistence file is corrupted too much.
*
* @see ChronicleHashBuilder#recoverPersistedTo(File, boolean)
@@ -50,7 +50,7 @@ import java.util.concurrent.ConcurrentMap;
* @param <V> the map value type
* @see ChronicleMapBuilder#create()
* @see ChronicleMapBuilder#createPersistedTo(File)
* @see ChronicleMapBuilder#createOrRecoverPersistedTo(File)
* @see ChronicleMapBuilder#createOrRecoverPersistedTo(File, boolean)
*/
public interface ChronicleMap<K, V> extends ConcurrentMap<K, V>,
ChronicleHash<K, MapEntry<K, V>, MapSegmentContext<K, V, ?>,
@@ -231,14 +231,14 @@ public final class ChronicleMapBuilder<K, V> implements
/**
* When Chronicle Maps are created using {@link #createPersistedTo(File)} or
* {@link #recoverPersistedTo(File, boolean)} or {@link #createOrRecoverPersistedTo(File)}
* methods, file lock on the Chronicle Map's file is acquired, that shouldn't be done from
* concurrent threads within the same JVM process. So creation of Chronicle Maps
* persisted to the same File should be synchronized across JVM's threads. Simple way would be
* to synchronize on some static (lock) object, but would serialize all Chronicle Maps creations
* (persisted to any files), ConcurrentHashMap#compute() gives more scalability.
* ConcurrentHashMap is used effectively for lock striping only, because the entries are not
* even landing the map, because compute() always returns null.
* {@link #recoverPersistedTo(File, boolean)} or {@link
* #createOrRecoverPersistedTo(File, boolean)} methods, file lock on the Chronicle Map's file is
* acquired, that shouldn't be done from concurrent threads within the same JVM process. So
* creation of Chronicle Maps persisted to the same File should be synchronized across JVM's
* threads. Simple way would be to synchronize on some static (lock) object, but would serialize
* all Chronicle Maps creations (persisted to any files), ConcurrentHashMap#compute() gives more
* scalability. ConcurrentHashMap is used effectively for lock striping only, because the
* entries are not even landing the map, because compute() always returns null.
*/
private static void fileLockedIO(
File file, FileChannel fileChannel, FileIOAction fileIOAction) throws IOException {
@@ -1474,13 +1474,23 @@ public final class ChronicleMapBuilder<K, V> implements
@Override
public ChronicleMap<K, V> createOrRecoverPersistedTo(File file) throws IOException {
return file.exists() ? recoverPersistedTo(file, true) : createPersistedTo(file);
return createOrRecoverPersistedTo(file, true);
}
@Override
public ChronicleMap<K, V> recoverPersistedTo(File file, boolean sameBuilderConfig)
public ChronicleMap<K, V> createOrRecoverPersistedTo(File file, boolean sameLibraryVersion)
throws IOException {
return clone().createWithFile(file, true, sameBuilderConfig);
if (file.exists()) {
return recoverPersistedTo(file, sameLibraryVersion);
} else {
return createPersistedTo(file);
}
}
@Override
public ChronicleMap<K, V> recoverPersistedTo(
File file, boolean sameBuilderConfigAndLibraryVersion) throws IOException {
return clone().createWithFile(file, true, sameBuilderConfigAndLibraryVersion);
}
@Override
@@ -311,10 +311,18 @@ public final class ChronicleSetBuilder<K>
}
@Override
public ChronicleSet<K> recoverPersistedTo(File file, boolean sameBuilderConfig)
public ChronicleSet<K> createOrRecoverPersistedTo(File file, boolean sameLibraryVersion)
throws IOException {
ChronicleMap<K, DummyValue> map =
chronicleMapBuilder.recoverPersistedTo(file, sameBuilderConfig);
chronicleMapBuilder.createOrRecoverPersistedTo(file, sameLibraryVersion);
return new SetFromMap<>((VanillaChronicleMap<K, DummyValue, ?>) map);
}
@Override
public ChronicleSet<K> recoverPersistedTo(File file, boolean sameBuilderConfigAndLibraryVersion)
throws IOException {
ChronicleMap<K, DummyValue> map =
chronicleMapBuilder.recoverPersistedTo(file, sameBuilderConfigAndLibraryVersion);
return new SetFromMap<>((VanillaChronicleMap<K, DummyValue, ?>) map);
}