Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle exceptions from Subscribers #2553

Merged
merged 7 commits into from
Mar 10, 2020

Conversation

minwoox
Copy link
Member

@minwoox minwoox commented Mar 5, 2020

Motivation:
An exception raised by a Subscriber's onNext() or other handler methods can cause an unexpected connection drop.
We should handle it.

Modifications:

  • Catch the exception thrown by onSubscribe() and onNext() from Subscribers.
    • Aabort the stream or call onError with the cause.
  • Catch the exception thrown by onComplete() and onError(), and log it.
  • Fork CompositeException from RxJava.

Result:

Motivation:
An exception raised by a `Subscriber`'s `onNext()` or other handler methods can cause an unexpected connection drop.
We should handle it.

Modifications:
- Catch the exception thrown by `onSubscribe()` and `onNext()` from `Subscriber`s.
  - Aabort the stream or call `onError` with the cause.
- Catch the exception thrown by `onComplete()` and `onError()`, and log it.

Result:
- Close line#2475
- The connection is not closed by unhandled exceptions from `Subscriber`s anymore.
@minwoox minwoox added the defect label Mar 5, 2020
@minwoox minwoox added this to the 0.99.0 milestone Mar 5, 2020
@minwoox minwoox requested review from trustin, anuraaga and ikhoon March 5, 2020 09:01
@codecov
Copy link

codecov bot commented Mar 5, 2020

Codecov Report

Merging #2553 into master will decrease coverage by 0.24%.
The diff coverage is 56.41%.

Impacted file tree graph

@@             Coverage Diff             @@
##             master   #2553      +/-   ##
===========================================
- Coverage     73.64%   73.4%   -0.25%     
- Complexity    10865   10947      +82     
===========================================
  Files           952     958       +6     
  Lines         41581   42108     +527     
  Branches       5219    5268      +49     
===========================================
+ Hits          30622   30908     +286     
- Misses         8290    8491     +201     
- Partials       2669    2709      +40
Impacted Files Coverage Δ Complexity Δ
...client/endpoint/healthcheck/HttpHealthChecker.java 88.05% <ø> (ø) 11 <0> (ø) ⬇️
...linecorp/armeria/common/HttpMessageAggregator.java 90.16% <ø> (ø) 15 <0> (ø) ⬇️
...p/armeria/client/retrofit2/AbstractSubscriber.java 82.92% <ø> (ø) 22 <0> (ø) ⬇️
...meria/common/stream/RegularFixedStreamMessage.java 88.67% <100%> (+0.92%) 14 <0> (ø) ⬇️
...ia/common/stream/TwoElementFixedStreamMessage.java 100% <100%> (ø) 14 <0> (ø) ⬇️
...ia/common/stream/OneElementFixedStreamMessage.java 92.85% <100%> (+2.38%) 8 <0> (ø) ⬇️
.../common/stream/DefaultStreamMessageDuplicator.java 74.58% <20%> (-3.87%) 8 <1> (ø)
...a/com/linecorp/armeria/common/util/Exceptions.java 36.5% <25%> (-0.79%) 29 <1> (+1)
...necorp/armeria/common/util/CompositeException.java 38.09% <38.09%> (ø) 9 <9> (?)
...p/armeria/server/encoding/HttpEncodedResponse.java 62.85% <42.85%> (-2.77%) 15 <0> (ø)
... and 41 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6f21290...8893ee6. Read the comment docs.

try {
lateSubscriber.onSubscribe(NoopSubscription.INSTANCE);
lateSubscriber.onError(cause);
} catch (Exception e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Global comment: Should we catch a Throwable?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reactive-streams/reactive-streams-jvm#107 (comment)
I think they are guiding not to catch the fatal ones. 🤔

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are Error that aren't fatal, so better to catch Throwable and we may need a propagateIfFatal like in zipkin

// Taken from RxJava throwIfFatal, which was taken from scala
  public static void propagateIfFatal(Throwable t) {
    if (t instanceof VirtualMachineError) {
      throw (VirtualMachineError) t;
    } else if (t instanceof ThreadDeath) {
      throw (ThreadDeath) t;
    } else if (t instanceof LinkageError) {
      throw (LinkageError) t;
    }
  }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, let me add this method to Exceptions. Thanks!

Copy link
Contributor

@ikhoon ikhoon Mar 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, I saw it from ReactiveX/RxJava#748 (comment)
and I think they intentionally omit that. But I didn't think about it deeply. 😆

import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.util.concurrent.ImmediateEventExecutor;

class SubscriberThrowingExceptionTest {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should move the tests to StreamMessageVerification.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, they don't have the TCK for this case and this is violating the spec, so the test will fail if they have, I think. 🤔
So it's like more for our implementation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StreamMessageVerification is our class, so we can perhaps test the failure scenarios there?

try {
lateSubscriber.onSubscribe(NoopSubscription.INSTANCE);
lateSubscriber.onError(cause);
} catch (Exception e) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are Error that aren't fatal, so better to catch Throwable and we may need a propagateIfFatal like in zipkin

// Taken from RxJava throwIfFatal, which was taken from scala
  public static void propagateIfFatal(Throwable t) {
    if (t instanceof VirtualMachineError) {
      throw (VirtualMachineError) t;
    } else if (t instanceof ThreadDeath) {
      throw (ThreadDeath) t;
    } else if (t instanceof LinkageError) {
      throw (LinkageError) t;
    }
  }

Copy link
Member

@trustin trustin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @minwoox !

@@ -0,0 +1,317 @@
/*
* Copyright 2016 LINE Corporation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2020

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😅

* under the License.
*/
/*
* Copyright (c) 2016-present, RxJava Contributors.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you update NOTICE.txt and licenses/ directory?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I thought we already had it.

NOTICE.txt Outdated Show resolved Hide resolved
Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! @minwoox

@trustin trustin merged commit 32b3d5a into line:master Mar 10, 2020
@minwoox minwoox deleted the handle_exception_subscriber branch March 10, 2020 05:28
@minwoox
Copy link
Member Author

minwoox commented Mar 10, 2020

Thanks a lot for reviewing!

fmguerreiro pushed a commit to fmguerreiro/armeria that referenced this pull request Sep 19, 2020
Motivation:
An exception raised by a `Subscriber`'s `onNext()` or other handler methods can cause an unexpected connection drop.
We should handle it.

Modifications:
- Catch the exception thrown by `onSubscribe()` and `onNext()` from `Subscriber`s.
  - Aabort the stream or call `onError` with the cause.
- Catch the exception thrown by `onComplete()` and `onError()`, and log it.
- Fork `CompositeException` from RxJava.

Result:
- Close line#2475
- The connection is not closed by unhandled exceptions from `Subscriber`s anymore.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Handle exceptions thrown by Subscriber
4 participants