-
Couldn't load subscription status.
- Fork 2k
Call completeStream only once on failure. #13814
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
base: jetty-12.0.x
Are you sure you want to change the base?
Conversation
a8cf31d to
bee65a6
Compare
Fix #13470 by calling completeStream only once even when there is a failure in the channel callback.
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Outdated
Show resolved
Hide resolved
|
The issue is fundamentally that For this to happen, we need a failure to be detected in the Lines 1573 to 1580 in 8dbd986
This means that We can see that there is a failure detected in
These are all plausible application errors, especially with something like server sent events. So once thread 258 has called which is called from this code after the handler has been invoked: Lines 691 to 713 in 8dbd986
Note that in order for this code to actually call Thus I believe the core fix is to not call Unfortunately I have been unable to produce a unit test for this, as I believe it needs precisely unlucky timing and an application error. |
Fix #13470 by calling completeStream only once even when there is a failure in the channel callback.
|
@sbordet @lorban Can you review the diagnosis that @janbartel and I have come up with. I'm 90% sure this is it, but I cannot reproduce (any thoughts how we might be able to do that?). |
|
Note that we added this |
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.
ChannelCallback.failed() seems not entirely correct either.
Can we write test cases for this scenario?
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Outdated
Show resolved
Hide resolved
| // We are committed and still handling, so let the HandlerInvoker complete, ignoring any pending reads/writes. | ||
| httpChannelState._streamSendState = StreamSendState.LAST_COMPLETE; |
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.
Again, not sure we should ignore pending writes.
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 pending reads.... it is a difficult one. I'll at least change the code to enumerate the possible states
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Outdated
Show resolved
Hide resolved
@sbordet I will look....
Very hard, because unless there is another thread racing the second completeStream is not called. I'm open to suggestions. |
Fix #13470 by calling completeStream only once even when there is a failure in the channel callback. refactor success and failure into single method.
Fix #13470 by calling completeStream only once even when there is a failure in the channel callback. refactor success and failure into single method. Improved EventSourceServlet
Fix #13470 by calling completeStream only once even when there is a failure in the channel callback. refactor success and failure into single method. Improved EventSourceServlet
|
@sbordet @lorban I'm getting concerned at the number of tests this PR is breaking in its current state. So I propose that this PR should simply be 83c1718 for 12.0.x (perhaps with the EventSourceServlet cleanups), and then we can do a wider cleanup and refactor in 12.1.x next month. |
Fix #13470 by calling completeStream only once even when there is a failure in the channel callback. refactor success and failure into single method. Improved EventSourceServlet
Fix #13470 by calling completeStream only once even when there is a failure in the channel callback. refactor success and failure into single method. Improved EventSourceServlet
Fix #13470 by calling completeStream only once even when there is a failure in the channel callback. refactor success and failure into single method. Improved EventSourceServlet
|
Regarding the minimal 12.0 fix, I think |
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Show resolved
Hide resolved
jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/ExceptionUtil.java
Outdated
Show resolved
Hide resolved
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Outdated
Show resolved
Hide resolved
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Show resolved
Hide resolved
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Outdated
Show resolved
Hide resolved
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Outdated
Show resolved
Hide resolved
| failedCallback = response._writeCallback; | ||
| response._writeCallback = httpChannelState._handlerInvoker; |
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.
In this way, we may wait forever for the write to complete and invoke the _handlerInvoker.
How about this:
| failedCallback = response._writeCallback; | |
| response._writeCallback = httpChannelState._handlerInvoker; | |
| Runnable task = response.lockedFailWrite(failure); | |
| failedCallback = Callback.from(task, httpChannelState._handlerInvoker); |
In 12.1.x we will leverage the new cancelSend() feature automatically.
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'm not sure that continuing on with a pending write pointing at the HttpChannelState is a good idea. Feels like an invitation for a race. What is wrong waiting for the write to complete. It won't be forever unless they have disabled idle timeout.
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'm not sure that continuing on with a pending write pointing at the HttpChannelState is a good idea.
I'm not sure what you mean above.
My point is that failing the callback may return the buffer to the pool, while the write is still pending.
We fixed this in 12.1.x with cancelSend(), but we don't leverage it here, while we would if we call lockedFailWrite().
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Show resolved
Hide resolved
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Outdated
Show resolved
Hide resolved
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Outdated
Show resolved
Hide resolved
…470-completeStreamOnce
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Outdated
Show resolved
Hide resolved
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Outdated
Show resolved
Hide resolved
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Outdated
Show resolved
Hide resolved
| else if (!httpChannelState.lockedIsLastStreamSendCompleted()) | ||
| { | ||
| // last write it is not going to happen after failure, so we can just fail anyway | ||
| httpChannelState._streamSendState = StreamSendState.FAILED; | ||
| } |
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 prefer to have an explicit else branch, otherwise feels like we are missing something.
Even if it is else LOG.debug("Nothing to do in this case").
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
Outdated
Show resolved
Hide resolved
| failedCallback = response._writeCallback; | ||
| response._writeCallback = httpChannelState._handlerInvoker; |
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'm not sure that continuing on with a pending write pointing at the HttpChannelState is a good idea.
I'm not sure what you mean above.
My point is that failing the callback may return the buffer to the pool, while the write is still pending.
We fixed this in 12.1.x with cancelSend(), but we don't leverage it here, while we would if we call lockedFailWrite().
Fix #13470 by calling completeStream only once even when there is a failure in the channel callback.