Skip to content

Commit 4fd3e0e

Browse files
authored
Propagate error to active chunk subscriber in SplittingPublisher (#6982)
1 parent a6ead96 commit 4fd3e0e

6 files changed

Lines changed: 87 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "Fixed an issue where `AsyncRequestBody.split()` did not propagate upstream errors to the in-progress chunk subscriber"
6+
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/NonRetryableSubAsyncRequestBody.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public int partNumber() {
9797
return partNumber;
9898
}
9999

100+
@Override
100101
public void error(Throwable error) {
101102
delegate.error(error);
102103
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/RetryableSubAsyncRequestBody.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ public long maxLength() {
108108
return configuration.maxLength();
109109
}
110110

111+
@Override
112+
public void error(Throwable error) {
113+
delegate.error(error);
114+
}
115+
111116
@Override
112117
public long receivedBytesLength() {
113118
return bufferedLength;

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/SplittingPublisher.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ public void onComplete() {
250250
@Override
251251
public void onError(Throwable t) {
252252
log.debug(() -> "Received onError()", t);
253+
currentBody.error(t);
253254
downstreamPublisher.error(t);
254255
}
255256

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/SubAsyncRequestBody.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ public interface SubAsyncRequestBody extends CloseableAsyncRequestBody {
3939
*/
4040
void complete();
4141

42+
/**
43+
* Signal that the stream has terminated with an error. No more {@link #send(ByteBuffer)} calls will be made.
44+
*/
45+
void error(Throwable error);
46+
4247
/**
4348
* The maximum length of the content this AsyncRequestBody can hold. If the upstream content length is known, this should be
4449
* the same as receivedBytesLength

core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/async/SplittingPublisherTest.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,75 @@ void retryableSubAsyncRequestBodyEnabled_shouldBeAbleToResubscribe() throws Exec
229229
}
230230
}
231231

232+
@ParameterizedTest
233+
@ValueSource(booleans = {true, false})
234+
void upstreamError_shouldPropagateToCurrentBodySubscriber(boolean enableRetryableSubAsyncRequestBody) throws Exception {
235+
RuntimeException upstreamError = new RuntimeException("upstream failure");
236+
AsyncRequestBody errorBody = new AsyncRequestBody() {
237+
@Override
238+
public Optional<Long> contentLength() {
239+
return Optional.of(20L);
240+
}
241+
242+
@Override
243+
public void subscribe(Subscriber<? super ByteBuffer> s) {
244+
s.onSubscribe(new Subscription() {
245+
private int calls = 0;
246+
247+
@Override
248+
public void request(long n) {
249+
if (calls++ == 0) {
250+
// Send partial data, then error
251+
s.onNext(ByteBuffer.wrap(new byte[3]));
252+
} else {
253+
s.onError(upstreamError);
254+
}
255+
}
256+
257+
@Override
258+
public void cancel() {
259+
}
260+
});
261+
}
262+
};
263+
264+
SplittingPublisher splittingPublisher =
265+
SplittingPublisher.builder()
266+
.asyncRequestBody(errorBody)
267+
.splitConfiguration(AsyncRequestBodySplitConfiguration.builder()
268+
.chunkSizeInBytes(10L)
269+
.bufferSizeInBytes(20L)
270+
.build())
271+
.retryableSubAsyncRequestBodyEnabled(enableRetryableSubAsyncRequestBody)
272+
.build();
273+
274+
CompletableFuture<Throwable> bodyError = new CompletableFuture<>();
275+
splittingPublisher.subscribe(requestBody -> {
276+
requestBody.subscribe(new Subscriber<ByteBuffer>() {
277+
@Override
278+
public void onSubscribe(Subscription s) {
279+
s.request(Long.MAX_VALUE);
280+
}
281+
282+
@Override
283+
public void onNext(ByteBuffer byteBuffer) {
284+
}
285+
286+
@Override
287+
public void onError(Throwable t) {
288+
bodyError.complete(t);
289+
}
290+
291+
@Override
292+
public void onComplete() {
293+
}
294+
});
295+
});
296+
297+
Throwable error = bodyError.get(5, TimeUnit.SECONDS);
298+
assertThat(error).isEqualTo(upstreamError);
299+
}
300+
232301
private static void verifySplitContent(AsyncRequestBody asyncRequestBody, int chunkSize) throws Exception {
233302
SplittingPublisher splittingPublisher = SplittingPublisher.builder()
234303
.asyncRequestBody(asyncRequestBody)

0 commit comments

Comments
 (0)