Skip to content

Commit 63dfd84

Browse files
authored
HADOOP-17458. S3A to treat "SdkClientException: Data read has a different length than the expected" as EOFException (#3040)
Some network exceptions can raise SdkClientException with message `Data read has a different length than the expected`. These should be recoverable. Contributed by Bogdan Stolojan
1 parent aa1a5dd commit 63dfd84

File tree

2 files changed

+46
-6
lines changed

2 files changed

+46
-6
lines changed

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AUtils.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,12 @@ public final class S3AUtils {
132132
S3AEncryptionMethods.SSE_S3.getMethod()
133133
+ " is enabled but an encryption key was set in "
134134
+ SERVER_SIDE_ENCRYPTION_KEY;
135-
private static final String EOF_MESSAGE_IN_XML_PARSER
135+
public static final String EOF_MESSAGE_IN_XML_PARSER
136136
= "Failed to sanitize XML document destined for handler class";
137137

138+
public static final String EOF_READ_DIFFERENT_LENGTH
139+
= "Data read has a different length than the expected";
140+
138141
private static final String BUCKET_PATTERN = FS_S3A_BUCKET_PREFIX + "%s.%s";
139142

140143
/**
@@ -194,7 +197,7 @@ public static IOException translateException(@Nullable String operation,
194197
// interrupted IO, or a socket exception underneath that class
195198
return translateInterruptedException(exception, innerCause, message);
196199
}
197-
if (signifiesConnectionBroken(exception)) {
200+
if (isMessageTranslatableToEOF(exception)) {
198201
// call considered an sign of connectivity failure
199202
return (EOFException)new EOFException(message).initCause(exception);
200203
}
@@ -415,13 +418,14 @@ public static boolean isThrottleException(Exception ex) {
415418

416419
/**
417420
* Cue that an AWS exception is likely to be an EOF Exception based
418-
* on the message coming back from an XML/JSON parser. This is likely
419-
* to be brittle, so only a hint.
421+
* on the message coming back from the client. This is likely to be
422+
* brittle, so only a hint.
420423
* @param ex exception
421424
* @return true if this is believed to be a sign the connection was broken.
422425
*/
423-
public static boolean signifiesConnectionBroken(SdkBaseException ex) {
424-
return ex.toString().contains(EOF_MESSAGE_IN_XML_PARSER);
426+
public static boolean isMessageTranslatableToEOF(SdkBaseException ex) {
427+
return ex.toString().contains(EOF_MESSAGE_IN_XML_PARSER) ||
428+
ex.toString().contains(EOF_READ_DIFFERENT_LENGTH);
425429
}
426430

427431
/**

hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestInvoker.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
package org.apache.hadoop.fs.s3a;
2020

21+
import java.io.EOFException;
2122
import java.io.IOException;
2223
import java.io.InterruptedIOException;
2324
import java.net.SocketTimeoutException;
@@ -28,6 +29,7 @@
2829
import com.amazonaws.AmazonClientException;
2930
import com.amazonaws.AmazonServiceException;
3031
import com.amazonaws.SdkBaseException;
32+
import com.amazonaws.SdkClientException;
3133
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughputExceededException;
3234
import com.amazonaws.services.s3.model.AmazonS3Exception;
3335
import org.junit.Assert;
@@ -163,6 +165,40 @@ public void test500isStatus500Exception() throws Exception {
163165
ex);
164166
}
165167

168+
@Test
169+
public void testExceptionsWithTranslatableMessage() throws Exception {
170+
SdkBaseException xmlParsing = new SdkBaseException(EOF_MESSAGE_IN_XML_PARSER);
171+
SdkBaseException differentLength = new SdkBaseException(EOF_READ_DIFFERENT_LENGTH);
172+
173+
verifyTranslated(EOFException.class, xmlParsing);
174+
verifyTranslated(EOFException.class, differentLength);
175+
}
176+
177+
178+
@Test
179+
public void testSdkDifferentLengthExceptionIsTranslatable() throws Throwable {
180+
final AtomicInteger counter = new AtomicInteger(0);
181+
invoker.retry("test", null, false, () -> {
182+
if (counter.incrementAndGet() < ACTIVE_RETRY_LIMIT) {
183+
throw new SdkClientException(EOF_READ_DIFFERENT_LENGTH);
184+
}
185+
});
186+
187+
assertEquals(ACTIVE_RETRY_LIMIT, counter.get());
188+
}
189+
190+
@Test
191+
public void testSdkXmlParsingExceptionIsTranslatable() throws Throwable {
192+
final AtomicInteger counter = new AtomicInteger(0);
193+
invoker.retry("test", null, false, () -> {
194+
if (counter.incrementAndGet() < ACTIVE_RETRY_LIMIT) {
195+
throw new SdkClientException(EOF_MESSAGE_IN_XML_PARSER);
196+
}
197+
});
198+
199+
assertEquals(ACTIVE_RETRY_LIMIT, counter.get());
200+
}
201+
166202
@Test(expected = org.apache.hadoop.net.ConnectTimeoutException.class)
167203
public void testExtractConnectTimeoutException() throws Throwable {
168204
throw extractException("", "",

0 commit comments

Comments
 (0)