Skip to content

Commit 5742f07

Browse files
Make unwrapCorrupt Check Suppressed Ex. (elastic#41889) (elastic#42605)
* Make unwrapCorrupt Check Suppressed Ex. (elastic#41889) * As discussed in elastic#24800 we want to check for suppressed corruption indicating exceptions here as well to more reliably categorize corruption related exceptions * Closes elastic#24800, 41201
1 parent be9d614 commit 5742f07

File tree

3 files changed

+64
-4
lines changed

3 files changed

+64
-4
lines changed

server/src/main/java/org/elasticsearch/ExceptionsHelper.java

+33-3
Original file line numberDiff line numberDiff line change
@@ -175,12 +175,42 @@ public static <T extends Throwable> T useOrSuppress(T first, T second) {
175175
return first;
176176
}
177177

178+
private static final List<Class<? extends IOException>> CORRUPTION_EXCEPTIONS =
179+
Arrays.asList(CorruptIndexException.class, IndexFormatTooOldException.class, IndexFormatTooNewException.class);
180+
181+
/**
182+
* Looks at the given Throwable's and its cause(s) as well as any suppressed exceptions on the Throwable as well as its causes
183+
* and returns the first corruption indicating exception (as defined by {@link #CORRUPTION_EXCEPTIONS}) it finds.
184+
* @param t Throwable
185+
* @return Corruption indicating exception if one is found, otherwise {@code null}
186+
*/
178187
public static IOException unwrapCorruption(Throwable t) {
179-
return (IOException) unwrap(t, CorruptIndexException.class,
180-
IndexFormatTooOldException.class,
181-
IndexFormatTooNewException.class);
188+
if (t != null) {
189+
do {
190+
for (Class<?> clazz : CORRUPTION_EXCEPTIONS) {
191+
if (clazz.isInstance(t)) {
192+
return (IOException) t;
193+
}
194+
}
195+
for (Throwable suppressed : t.getSuppressed()) {
196+
IOException corruptionException = unwrapCorruption(suppressed);
197+
if (corruptionException != null) {
198+
return corruptionException;
199+
}
200+
}
201+
} while ((t = t.getCause()) != null);
202+
}
203+
return null;
182204
}
183205

206+
/**
207+
* Looks at the given Throwable and its cause(s) and returns the first Throwable that is of one of the given classes or {@code null}
208+
* if no matching Throwable is found. Unlike {@link #unwrapCorruption} this method does only check the given Throwable and its causes
209+
* but does not look at any suppressed exceptions.
210+
* @param t Throwable
211+
* @param clazzes Classes to look for
212+
* @return Matching Throwable if one is found, otherwise {@code null}
213+
*/
184214
public static Throwable unwrap(Throwable t, Class<?>... clazzes) {
185215
if (t != null) {
186216
do {

server/src/test/java/org/elasticsearch/ExceptionsHelperTests.java

+28
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch;
2121

2222
import org.apache.commons.codec.DecoderException;
23+
import org.apache.lucene.index.CorruptIndexException;
2324
import org.elasticsearch.action.OriginalIndices;
2425
import org.elasticsearch.action.ShardOperationFailedException;
2526
import org.elasticsearch.action.search.ShardSearchFailure;
@@ -183,4 +184,31 @@ public void testGroupByNullIndex() {
183184
ShardOperationFailedException[] groupBy = ExceptionsHelper.groupBy(failures);
184185
assertThat(groupBy.length, equalTo(2));
185186
}
187+
188+
public void testUnwrapCorruption() {
189+
final Throwable corruptIndexException = new CorruptIndexException("corrupt", "resource");
190+
assertThat(ExceptionsHelper.unwrapCorruption(corruptIndexException), equalTo(corruptIndexException));
191+
192+
final Throwable corruptionAsCause = new RuntimeException(corruptIndexException);
193+
assertThat(ExceptionsHelper.unwrapCorruption(corruptionAsCause), equalTo(corruptIndexException));
194+
195+
final Throwable corruptionSuppressed = new RuntimeException();
196+
corruptionSuppressed.addSuppressed(corruptIndexException);
197+
assertThat(ExceptionsHelper.unwrapCorruption(corruptionSuppressed), equalTo(corruptIndexException));
198+
199+
final Throwable corruptionSuppressedOnCause = new RuntimeException(new RuntimeException());
200+
corruptionSuppressedOnCause.getCause().addSuppressed(corruptIndexException);
201+
assertThat(ExceptionsHelper.unwrapCorruption(corruptionSuppressedOnCause), equalTo(corruptIndexException));
202+
203+
final Throwable corruptionCauseOnSuppressed = new RuntimeException();
204+
corruptionCauseOnSuppressed.addSuppressed(new RuntimeException(corruptIndexException));
205+
assertThat(ExceptionsHelper.unwrapCorruption(corruptionCauseOnSuppressed), equalTo(corruptIndexException));
206+
207+
assertThat(ExceptionsHelper.unwrapCorruption(new RuntimeException()), nullValue());
208+
assertThat(ExceptionsHelper.unwrapCorruption(new RuntimeException(new RuntimeException())), nullValue());
209+
210+
final Throwable withSuppressedException = new RuntimeException();
211+
withSuppressedException.addSuppressed(new RuntimeException());
212+
assertThat(ExceptionsHelper.unwrapCorruption(withSuppressedException), nullValue());
213+
}
186214
}

server/src/test/java/org/elasticsearch/indices/recovery/RecoverySourceHandlerTests.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -436,10 +436,12 @@ protected void failEngine(IOException cause) {
436436
handler.sendFiles(store, metas.toArray(new StoreFileMetaData[0]), () -> 0);
437437
fail("exception index");
438438
} catch (RuntimeException ex) {
439-
assertNull(ExceptionsHelper.unwrapCorruption(ex));
439+
final IOException unwrappedCorruption = ExceptionsHelper.unwrapCorruption(ex);
440440
if (throwCorruptedIndexException) {
441+
assertNotNull(unwrappedCorruption);
441442
assertEquals(ex.getMessage(), "[File corruption occurred on recovery but checksums are ok]");
442443
} else {
444+
assertNull(unwrappedCorruption);
443445
assertEquals(ex.getMessage(), "boom");
444446
}
445447
} catch (CorruptIndexException ex) {

0 commit comments

Comments
 (0)