Skip to content

Commit 5ff10ae

Browse files
PDGGKclaude
andcommitted
Simplify parseAndThrow: check e.getCause() instead of traversing causal chain
Future.get() wraps with exactly one ExecutionException, so inspecting e.getCause() is sufficient. If the cause is a UserCodeExecutionException (or subclass), rethrow it directly — this preserves all retryable types and is automatically future-proof for new subclasses. Remove deep-nesting tests that tested the now-removed causal chain traversal, as those scenarios cannot occur in this code path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 0d03fe1 commit 5ff10ae

2 files changed

Lines changed: 3 additions & 242 deletions

File tree

  • sdks/java/io/rrio/src
    • main/java/org/apache/beam/io/requestresponse
    • test/java/org/apache/beam/io/requestresponse

sdks/java/io/rrio/src/main/java/org/apache/beam/io/requestresponse/Call.java

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
import org.apache.beam.sdk.values.PCollectionTuple;
4747
import org.apache.beam.sdk.values.TupleTag;
4848
import org.apache.beam.sdk.values.TupleTagList;
49-
import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Throwables;
5049
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
5150
import org.joda.time.Duration;
5251

@@ -614,33 +613,10 @@ private void executeAsync(Callable<Void> callable) throws UserCodeExecutionExcep
614613
private static <T> void parseAndThrow(Future<T> future, ExecutionException e)
615614
throws UserCodeExecutionException {
616615
future.cancel(true);
617-
618-
try {
619-
UserCodeExecutionException genericException = null;
620-
for (Throwable throwable : Throwables.getCausalChain(e)) {
621-
if (throwable instanceof UserCodeQuotaException) {
622-
throw (UserCodeQuotaException) throwable;
623-
} else if (throwable instanceof UserCodeTimeoutException) {
624-
throw (UserCodeTimeoutException) throwable;
625-
} else if (throwable instanceof UserCodeRemoteSystemException) {
626-
throw (UserCodeRemoteSystemException) throwable;
627-
} else if (genericException == null && throwable instanceof UserCodeExecutionException) {
628-
genericException = (UserCodeExecutionException) throwable;
629-
}
630-
}
631-
if (genericException != null) {
632-
throw genericException;
633-
}
634-
} catch (IllegalArgumentException iae) {
635-
// Circular reference detected in causal chain
636-
throw new UserCodeExecutionException(
637-
"circular reference detected in exception causal chain", e);
638-
}
639-
640616
Throwable cause = e.getCause();
641-
if (cause == null) {
642-
throw new UserCodeExecutionException(e);
617+
if (cause instanceof UserCodeExecutionException) {
618+
throw (UserCodeExecutionException) cause;
643619
}
644-
throw new UserCodeExecutionException(cause);
620+
throw new UserCodeExecutionException(cause == null ? e : cause);
645621
}
646622
}

sdks/java/io/rrio/src/test/java/org/apache/beam/io/requestresponse/CallTest.java

Lines changed: 0 additions & 215 deletions
Original file line numberDiff line numberDiff line change
@@ -118,20 +118,6 @@ public void givenCallerThrowsNonUserCodeException_emitsWrappedUserCodeExecutionE
118118
pipeline.run();
119119
}
120120

121-
@Test
122-
public void givenCallerThrowsCircularCausalChain_emitsUserCodeExecutionException() {
123-
Result<Response> result =
124-
pipeline
125-
.apply(Create.of(new Request("a")))
126-
.apply(Call.of(new CallerThrowsCircularCause(), NON_DETERMINISTIC_RESPONSE_CODER));
127-
128-
PCollection<ApiIOError> failures = result.getFailures();
129-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeExecutionException.class))
130-
.isEqualTo(1L);
131-
132-
pipeline.run();
133-
}
134-
135121
@Test
136122
public void givenCallerThrowsQuotaException_emitsIntoFailurePCollection() {
137123
Result<Response> result =
@@ -203,136 +189,6 @@ public void givenCallerThrowsRemoteSystemException_thenPreservesExceptionType()
203189
pipeline.run();
204190
}
205191

206-
@Test
207-
public void givenNestedExecutionException_thenPreservesExceptionType() {
208-
Result<Response> result =
209-
pipeline
210-
.apply(Create.of(new Request("a")))
211-
.apply(
212-
Call.of(
213-
new CallerThrowsNestedExecutionException(), NON_DETERMINISTIC_RESPONSE_CODER));
214-
215-
PCollection<ApiIOError> failures = result.getFailures();
216-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeExecutionException.class))
217-
.isEqualTo(1L);
218-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeTimeoutException.class))
219-
.isEqualTo(0L);
220-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeRemoteSystemException.class))
221-
.isEqualTo(0L);
222-
223-
pipeline.run();
224-
}
225-
226-
@Test
227-
public void givenCallerThrowsGenericWrappingTimeout_thenPreservesExceptionType() {
228-
Result<Response> result =
229-
pipeline
230-
.apply(Create.of(new Request("a")))
231-
.apply(
232-
Call.of(
233-
new CallerThrowsGenericWrappingTimeout(), NON_DETERMINISTIC_RESPONSE_CODER));
234-
235-
PCollection<ApiIOError> failures = result.getFailures();
236-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeTimeoutException.class))
237-
.isEqualTo(1L);
238-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeExecutionException.class))
239-
.isEqualTo(0L);
240-
241-
pipeline.run();
242-
}
243-
244-
@Test
245-
public void givenCallerThrowsGenericWrappingQuota_thenPreservesExceptionType() {
246-
Result<Response> result =
247-
pipeline
248-
.apply(Create.of(new Request("a")))
249-
.apply(
250-
Call.of(new CallerThrowsGenericWrappingQuota(), NON_DETERMINISTIC_RESPONSE_CODER));
251-
252-
PCollection<ApiIOError> failures = result.getFailures();
253-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeQuotaException.class)).isEqualTo(1L);
254-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeExecutionException.class))
255-
.isEqualTo(0L);
256-
257-
pipeline.run();
258-
}
259-
260-
@Test
261-
public void givenCallerThrowsGenericWrappingRemoteSystem_thenPreservesExceptionType() {
262-
Result<Response> result =
263-
pipeline
264-
.apply(Create.of(new Request("a")))
265-
.apply(
266-
Call.of(
267-
new CallerThrowsGenericWrappingRemoteSystem(),
268-
NON_DETERMINISTIC_RESPONSE_CODER));
269-
270-
PCollection<ApiIOError> failures = result.getFailures();
271-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeRemoteSystemException.class))
272-
.isEqualTo(1L);
273-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeExecutionException.class))
274-
.isEqualTo(0L);
275-
276-
pipeline.run();
277-
}
278-
279-
@Test
280-
public void
281-
givenCallerThrowsUncheckedExecutionExceptionWrappingTimeout_thenPreservesExceptionType() {
282-
Result<Response> result =
283-
pipeline
284-
.apply(Create.of(new Request("a")))
285-
.apply(
286-
Call.of(
287-
new CallerThrowsUncheckedExecutionExceptionWrappingTimeout(),
288-
NON_DETERMINISTIC_RESPONSE_CODER));
289-
290-
PCollection<ApiIOError> failures = result.getFailures();
291-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeTimeoutException.class))
292-
.isEqualTo(1L);
293-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeExecutionException.class))
294-
.isEqualTo(0L);
295-
296-
pipeline.run();
297-
}
298-
299-
@Test
300-
public void
301-
givenCallerThrowsUncheckedExecutionExceptionWrappingRemoteSystem_thenPreservesExceptionType() {
302-
Result<Response> result =
303-
pipeline
304-
.apply(Create.of(new Request("a")))
305-
.apply(
306-
Call.of(
307-
new CallerThrowsUncheckedExecutionExceptionWrappingRemoteSystem(),
308-
NON_DETERMINISTIC_RESPONSE_CODER));
309-
310-
PCollection<ApiIOError> failures = result.getFailures();
311-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeRemoteSystemException.class))
312-
.isEqualTo(1L);
313-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeExecutionException.class))
314-
.isEqualTo(0L);
315-
316-
pipeline.run();
317-
}
318-
319-
@Test
320-
public void givenCallerThrowsTripleNestedTimeout_thenPreservesExceptionType() {
321-
Result<Response> result =
322-
pipeline
323-
.apply(Create.of(new Request("a")))
324-
.apply(
325-
Call.of(new CallerThrowsTripleNestedTimeout(), NON_DETERMINISTIC_RESPONSE_CODER));
326-
327-
PCollection<ApiIOError> failures = result.getFailures();
328-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeTimeoutException.class))
329-
.isEqualTo(1L);
330-
PAssert.thatSingleton(countStackTracesOf(failures, UserCodeExecutionException.class))
331-
.isEqualTo(0L);
332-
333-
pipeline.run();
334-
}
335-
336192
@Test
337193
public void givenSetupThrowsUserCodeExecutionException_throwsError() {
338194
pipeline
@@ -559,17 +415,6 @@ public Response call(Request request) {
559415
}
560416
}
561417

562-
private static class CallerThrowsCircularCause implements Caller<Request, Response> {
563-
564-
@Override
565-
public Response call(Request request) {
566-
Exception a = new Exception("a");
567-
Exception b = new Exception("b", a);
568-
a.initCause(b); // a -> b -> a (circular reference)
569-
throw new RuntimeException("boom", a);
570-
}
571-
}
572-
573418
private static class CallerThrowsTimeout implements Caller<Request, Response> {
574419

575420
@Override
@@ -586,66 +431,6 @@ public Response call(Request request) throws UserCodeExecutionException {
586431
}
587432
}
588433

589-
private static class CallerThrowsNestedExecutionException implements Caller<Request, Response> {
590-
591-
@Override
592-
public Response call(Request request) throws UserCodeExecutionException {
593-
throw new UncheckedExecutionException(new UserCodeExecutionException("nested"));
594-
}
595-
}
596-
597-
private static class CallerThrowsGenericWrappingTimeout implements Caller<Request, Response> {
598-
599-
@Override
600-
public Response call(Request request) throws UserCodeExecutionException {
601-
throw new UserCodeExecutionException("generic", new UserCodeTimeoutException("timeout"));
602-
}
603-
}
604-
605-
private static class CallerThrowsGenericWrappingQuota implements Caller<Request, Response> {
606-
607-
@Override
608-
public Response call(Request request) throws UserCodeExecutionException {
609-
throw new UserCodeExecutionException("generic", new UserCodeQuotaException("quota"));
610-
}
611-
}
612-
613-
private static class CallerThrowsGenericWrappingRemoteSystem
614-
implements Caller<Request, Response> {
615-
616-
@Override
617-
public Response call(Request request) throws UserCodeExecutionException {
618-
throw new UserCodeExecutionException("generic", new UserCodeRemoteSystemException("remote"));
619-
}
620-
}
621-
622-
private static class CallerThrowsUncheckedExecutionExceptionWrappingTimeout
623-
implements Caller<Request, Response> {
624-
625-
@Override
626-
public Response call(Request request) throws UserCodeExecutionException {
627-
throw new UncheckedExecutionException(new UserCodeTimeoutException("timeout"));
628-
}
629-
}
630-
631-
private static class CallerThrowsUncheckedExecutionExceptionWrappingRemoteSystem
632-
implements Caller<Request, Response> {
633-
634-
@Override
635-
public Response call(Request request) throws UserCodeExecutionException {
636-
throw new UncheckedExecutionException(new UserCodeRemoteSystemException("remote"));
637-
}
638-
}
639-
640-
private static class CallerThrowsTripleNestedTimeout implements Caller<Request, Response> {
641-
642-
@Override
643-
public Response call(Request request) throws UserCodeExecutionException {
644-
throw new UncheckedExecutionException(
645-
new RuntimeException(new UserCodeTimeoutException("deep timeout")));
646-
}
647-
}
648-
649434
private static class CallerInvokesQuotaException implements Caller<Request, Response> {
650435

651436
@Override

0 commit comments

Comments
 (0)