Async servlet non-blocking IO and request recycling

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

Async servlet non-blocking IO and request recycling

François Rajotte
Hi,

I have a question regarding async servlets and request recycling. In
particular, I'm interested in request recycling by tomcat when an IO
error occurs. In my use case, I'm using non-blocking reads with a
ReadListener but blocking writes (no WriteListener).

The main question can be summarized as the following:
Does tomcat always call the AsyncListener's onComplete method before
recycling the request and response objects?

I assumed that this must be true, but I have discovered that it is not so.

My scenario very much resembles the one described in the following
thread from 2016 except that the IO exception occurs on a container
thread:
http://mail-archives.apache.org/mod_mbox/tomcat-users/201611.mbox/%3c0d28a20f-fa3d-9e58-b5d7-82950ca5142f@...%3e

Also, my scenario is extremely similar to the scenarios described in
the following bug reports:
https://bz.apache.org/bugzilla/show_bug.cgi?id=59219
https://bz.apache.org/bugzilla/show_bug.cgi?id=59220

Those bugs were mainly fixed by modifying the service method of the
CoyoteAdapter class, but in my scenario, it looks like it's the
asyncDispatch method that is being hit instead.

Description of the scenario

In a servlet, start async processing and register a read listener for
non-blocking IO processing.
In the read listener, perform blocking writes on the response when the
onAllDataRead callback is called. Importantly, catch IOExceptions and
don't let them bubble up.

Also start a non-container thread that will access the request and/or
response object some time in the future. This processing will be
prevented if the AsyncListener's onComplete method is called. We can
assume that this is properly synchronized.

Have a client access this servlet, but immediately disconnect without
waiting for the response.
This will cause the write operations in the ReadListener's
onAllDataRead method to throw IOExceptions, but as mentioned
previously this exception is caught and silenced by the servlet. The
AsyncContext complete method is also NOT called by the servlet at any
time.

Tomcat then processes the IO error and recycles the request and
response objects.

Some time later, the non-container thread attempts to access the
request and/or response objects and gets hit with a non-checked
exception because they have been recycled.

This behavior has been observed in tomcat 8.0.53 and 9.0.24. I have
not tested it on 9.0.30 yet.

There are a few changes related to async processing since 9.0.24 but
none of them seem related to that scenario. For the record, here they
are (from the changelog):
63682: Fix a potential hang when using the asynchronous Servlet API to
write the response body and the stream and/or connection window
reaches 0 bytes in size. (markt)
63816 and 63817: Correctly handle I/O errors after asynchronous
processing has been started but before the container thread that
started asynchronous processing has completed processing the current
request/response. (markt)
Ensure that ServletRequest.isAsyncStarted() returns false once
AsyncContext.complete() or AsyncContext.dispatch() has been called
during AsyncListener.onTimeout() or AsyncListener.onError(). (markt)
63931: Improve timeout handling for asyncIO to ensure that blocking
operations see a SocketTimeoutException if one occurs. (remm/markt)

Please let me know if my understanding of the servlet API is wrong
regarding asynchronous operation and non-blocking IO processing! I can
adjust my servlet to do the appropriate cleanup when an IOException is
caught, but it seems so much hackier and uglier than just processing
an onComplete or onError callback.

Regards,
François

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]