-
Notifications
You must be signed in to change notification settings - Fork 79
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
Fixes #118 Improve FlowControllingHttpContentProducer state machine. #120
Conversation
…te machine. New event DelayedTearDownEvent created to replace IdleStateEvent.
producer.receivedChunks(), | ||
producer.emittedBytes(), | ||
producer.emittedChunks()))), | ||
() -> contentProducer.ifPresent(producer -> producer.tearDownResources(cause)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tearDownResources
event is scheduled in a controlled fashion. That is, the tearDownResources
is always scheduled after a short time to ensure state machine is orderly brought down. Therefore cause
doesn't have to be passed in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say that if we have to publish an error, it will be more informative if it is the same error that caused the "channelInactive" -so they can be easily linked when reading the logs-, than if we make up an Exception as in your example in the comment below (TimedOutExceptionWhatever).
I have nonetheless implemented the change as per your suggestion keeping the ResponseTimeoutException as previously.
.transition(BUFFERING_COMPLETED, ContentSubscribedEvent.class, this::contentSubscribedEventWhileBufferingCompleted) | ||
.transition(BUFFERING_COMPLETED, ContentEndEvent.class, s -> BUFFERING_COMPLETED) | ||
|
||
.transition(STREAMING, RxBackpressureRequestEvent.class, this::rxBackpressureRequestEventWhileStreaming) | ||
.transition(STREAMING, ContentChunkEvent.class, this::contentChunkEventWhileStreaming) | ||
.transition(STREAMING, ChannelInactiveEvent.class, e -> emitErrorAndTerminate(e.cause())) | ||
.transition(STREAMING, ChannelExceptionEvent.class, e -> emitErrorAndTerminate(e.cause())) | ||
.transition(STREAMING, ChannelIdleEvent.class, e -> emitErrorAndTerminate(new ResponseTimeoutException(origin))) | ||
.transition(STREAMING, DelayedTearDownEvent.class, e -> emitErrorAndTerminate(e.cause())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would use a different method for the state transition handler. For example:
+ .transition(STREAMING, DelayedTearDownEvent.class, e -> tearDown())
And then in the tearDown
method:
private void tearDown() {
emitErrorAndTerminate(new TimedOutExceptionWhatever()); // Choose an appropriate exception here.
}
So I would have a dedicated method specifically for the tearDown
transition. This exposes the state transition logic more explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds like an improvement, but in your example we are emitting an error, I assume, to signal an invalid combination of state+event. Doesn't it make more sense to use a new 'tearDown' method for the cases in which we are calling 'releaseAndTerminate' ?
…te machine. New event DelayedTearDownEvent created to replace IdleStateEvent.
… necessary states: BUFFERING_COMPLETED EMITTING_BUFFERED_CONTENT
… necessary states: BUFFERING_COMPLETED EMITTING_BUFFERED_CONTENT
@@ -53,6 +53,7 @@ | |||
private final Runnable askForMore; | |||
private final Runnable onCompleteAction; | |||
private final Consumer<Throwable> onTerminateAction; | |||
private Runnable delayedTearDownAction; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be final.
.transition(BUFFERING_COMPLETED, ChannelExceptionEvent.class, s -> BUFFERING_COMPLETED) | ||
.transition(BUFFERING_COMPLETED, ChannelIdleEvent.class, this::releaseAndTerminate) | ||
.transition(BUFFERING_COMPLETED, DelayedTearDownEvent.class, this::tearDown) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am thinking we should call this::releaseAndTerminate
directly since the this::tearDown
method is identical.
.transition(EMITTING_BUFFERED_CONTENT, ChannelExceptionEvent.class, s -> EMITTING_BUFFERED_CONTENT) | ||
.transition(EMITTING_BUFFERED_CONTENT, ChannelIdleEvent.class, e -> emitErrorAndTerminate(e.cause())) | ||
.transition(EMITTING_BUFFERED_CONTENT, DelayedTearDownEvent.class, this::tearDownWithError) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps it is better to use emitErrorAndTerminate
instead of tearDownWithError
because they are identical.
|
||
private ProducerState tearDown(CausalEvent event) { | ||
return releaseAndTerminate(event); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned in the previous comments let's get rid of tearDown
and tearDownWithError
methods.
producer.receivedBytes(), | ||
producer.receivedChunks(), | ||
producer.emittedBytes(), | ||
producer.emittedChunks()))), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please ensure these stats are passed in to the ResponseTimeoutException.
Even better please add a test that verifies that the counters are present in the exception when it is torn down.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or instead log a warning using FlowControllingHttpContentProducer.warningMessage
. This is the preferred option IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The log messages can be tested with LoggingTestSupport
. Please look for examples of usage in the codebase. It is quite easy to use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively raise a separate GitHub enhancement issue.
TearDown methods removed Exception in the teardownevent contains bytecount.
New event, DelayedTearDownEvent, created to replace IdleStateEvent.
Currently there's no handling of idle events in the FSM in
FlowControllingHttpContentProducer
, so the logic has been kept untouched.