IOException from get / put / append sets corrupted state for

PersistentHashMap (IDEA-173669)
This commit is contained in:
Maxim.Mossienko
2017-06-22 20:24:41 +02:00
parent 91daa4312d
commit d7c09841c0
2 changed files with 150 additions and 14 deletions
@@ -20,7 +20,10 @@ import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.IntObjectCache;
import com.intellij.util.io.storage.AbstractStorage;
import org.jetbrains.annotations.NotNull;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
import java.util.*;
@@ -344,4 +347,92 @@ public class PersistentMapTest extends PersistentMapTestBase {
fail();
} catch (IncorrectOperationException ignore) {}
}
public void testFailedReadWriteSetsCorruptedFlag() throws IOException {
EnumeratorStringDescriptor throwingException = new EnumeratorStringDescriptor() {
@Override
public void save(@NotNull DataOutput storage, @NotNull String value) throws IOException {
throw new IOException("test");
}
@Override
public String read(@NotNull DataInput storage) throws IOException {
throw new IOException("test");
}
};
PersistentMapPerformanceTest.MapConstructor<String, String> mapConstructorWithBrokenKeyDescriptor =
(file) -> IOUtil.openCleanOrResetBroken(
() -> new PersistentHashMap<>(file, throwingException, EnumeratorStringDescriptor.INSTANCE), file);
PersistentMapPerformanceTest.MapConstructor<String, String> mapConstructorWithBrokenValueDescriptor =
(file) -> IOUtil.openCleanOrResetBroken(
() -> new PersistentHashMap<>(file, EnumeratorStringDescriptor.INSTANCE, throwingException), file);
runIteration(mapConstructorWithBrokenKeyDescriptor);
runIteration(mapConstructorWithBrokenValueDescriptor);
}
private void runIteration(PersistentMapPerformanceTest.MapConstructor<String, String> brokenMapDescritor) throws IOException {
String key = "AAA";
String value = "AAA_VALUE";
PersistentMapPerformanceTest.MapConstructor<String, String> defaultMapConstructor =
(file) -> IOUtil.openCleanOrResetBroken(
() -> new PersistentHashMap<>(file, EnumeratorStringDescriptor.INSTANCE, EnumeratorStringDescriptor.INSTANCE), file);
createInitializedMap(key, value, defaultMapConstructor);
myMap = brokenMapDescritor.createMap(myFile);
try {
myMap.get(key);
fail();
} catch (IOException ignore) {
assertTrue(myMap.isCorrupted());
}
createInitializedMap(key, value, defaultMapConstructor);
myMap = brokenMapDescritor.createMap(myFile);
try {
myMap.put(key, value + value);
fail();
} catch (IOException ignore) {
assertTrue(myMap.isCorrupted());
}
createInitializedMap(key, value, defaultMapConstructor);
myMap = brokenMapDescritor.createMap(myFile);
try {
myMap.appendData(key, new PersistentHashMap.ValueDataAppender() {
@Override
public void append(DataOutput out) throws IOException {
throw new IOException();
}
});
fail();
} catch (IOException ignore) {
assertTrue(myMap.isCorrupted());
}
}
private void closeMapSilently() throws IOException {
try {
myMap.close();
} catch (IOException ignore) {}
}
private void createInitializedMap(String key,
String value,
PersistentMapPerformanceTest.MapConstructor<String, String> defaultMapConstructor)
throws IOException {
closeMapSilently();
myMap = defaultMapConstructor.createMap(myFile);
myMap.put(key, value);
closeMapSilently();
}
}
@@ -129,8 +129,21 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
this(file, keyDescriptor, valueExternalizer, initialSize, 0);
}
public PersistentHashMap(@NotNull final File file, @NotNull KeyDescriptor<Key> keyDescriptor, @NotNull DataExternalizer<Value> valueExternalizer, final int initialSize, int version) throws IOException {
super(checkDataFiles(file), keyDescriptor, initialSize, null, version);
public PersistentHashMap(@NotNull final File file,
@NotNull KeyDescriptor<Key> keyDescriptor,
@NotNull DataExternalizer<Value> valueExternalizer,
final int initialSize,
int version) throws IOException {
this(file, keyDescriptor, valueExternalizer, initialSize, version, null);
}
public PersistentHashMap(@NotNull final File file,
@NotNull KeyDescriptor<Key> keyDescriptor,
@NotNull DataExternalizer<Value> valueExternalizer,
final int initialSize,
int version,
@Nullable PagedFileStorage.StorageLockContext lockContext) throws IOException {
super(checkDataFiles(file), keyDescriptor, initialSize, lockContext, version);
myStorageFile = file;
myKeyDescriptor = keyDescriptor;
@@ -255,6 +268,7 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
myStreamPool.recycle(bytes);
}
catch (IOException e) {
markCorrupted();
throw new RuntimeException(e);
}
finally {
@@ -344,7 +358,12 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
public final void put(Key key, Value value) throws IOException {
if (myIsReadOnly) throw new IncorrectOperationException();
synchronized (myEnumerator) {
doPut(key, value);
try {
doPut(key, value);
} catch (IOException ex) {
myEnumerator.markCorrupted();
throw ex;
}
}
}
@@ -420,7 +439,12 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
public final void appendData(Key key, @NotNull ValueDataAppender appender) throws IOException {
if (myIsReadOnly) throw new IncorrectOperationException();
synchronized (myEnumerator) {
doAppendData(key, appender);
try {
doAppendData(key, appender);
} catch (IOException ex) {
myEnumerator.markCorrupted();
throw ex;
}
}
}
@@ -449,8 +473,14 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
@Override
public final boolean processKeys(Processor<Key> processor) throws IOException {
synchronized (myEnumerator) {
myAppendCache.clear();
return myEnumerator.iterateData(processor);
try {
myAppendCache.clear();
return myEnumerator.iterateData(processor);
}
catch (IOException e) {
myEnumerator.markCorrupted();
throw e;
}
}
}
@@ -463,13 +493,19 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
public final boolean processKeysWithExistingMapping(Processor<Key> processor) throws IOException {
synchronized (myEnumerator) {
myAppendCache.clear();
return myEnumerator.processAllDataObject(processor, new PersistentEnumerator.DataFilter() {
@Override
public boolean accept(final int id) {
return readValueId(id) != NULL_ADDR;
}
});
try {
myAppendCache.clear();
return myEnumerator.processAllDataObject(processor, new PersistentEnumerator.DataFilter() {
@Override
public boolean accept(final int id) {
return readValueId(id) != NULL_ADDR;
}
});
}
catch (IOException e) {
myEnumerator.markCorrupted();
throw e;
}
}
}
@@ -479,6 +515,9 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
myBusyReading = true;
try {
return doGet(key);
} catch(IOException ex) {
myEnumerator.markCorrupted();
throw ex;
} finally {
myBusyReading = false;
}
@@ -669,7 +708,13 @@ public class PersistentHashMap<Key, Value> extends PersistentEnumeratorDelegate<
try {
try {
myAppendCacheFlusher.stop();
myAppendCache.clear();
try {
myAppendCache.clear();
} catch (RuntimeException ex) {
Throwable cause = ex.getCause();
if (cause instanceof IOException) throw (IOException)cause;
throw ex;
}
}
finally {
final PersistentHashMapValueStorage valueStorage = myValueStorage;