1

The story is that, we have a webapp hosted by Tomcat + Apache Httpd as front-end. The webapp needs an extra long time to process certain REST request (query to the database for a long time then generate a summary after getting the database response).

Under the current implementation, even if the REST client stops the request, the query is still being processed until the database query finishes and the response is tried to be sent back and find out that the output stream is closed. On the other hand, the webapp does not send back anything within a long time, causing the Apache Httpd session times out (we configure 5 minutes as time out).

So what would be the best practice to achieve below goals:

  • If the client terminates the requests, I want the processing of the request stops, and the query to database terminates as well.

  • Tomcat can send back premature response if the request has not been terminated by the client but the query already takes a long time.

    Update: I thought server may send back 100 continue, but I am wrong. Looks like 100 continue is not intend for that purpose at all.

I am thinking a model like this:

  • Introduce a servelet filter to accept the REST request.
  • The filter create another thread to invoke the target REST asynchronously, providing an output stream.
  • The filter periodically do some sleep and inspect the output stream of the new thread, and sends back 100 continue from time to time, or stream the result from output stream to the client.

Is this workable, or any other preferable approach?

Gordon Liang
  • 348
  • 2
  • 11
  • See https://stackoverflow.com/a/21529531/227491 – Peter Sep 11 '17 at 19:14
  • Also https://stackoverflow.com/questions/2962196/detecting-client-disconnect-in-tomcat-servlet – Peter Sep 11 '17 at 19:16
  • @Peter thanks but it looks like those answers do not satisfy my need. Further more, I need to add that the webapp is not developed by us and we don't want to change the logic in the REST processing logic to check the output stream. – Gordon Liang Sep 11 '17 at 19:20
  • You can certainly put the server side process under a master/supervisor which sends back "heartbeat" messages (e.g. in the form of HTML comments) every 15 seconds, and keeps the client from timing out the connection. But this is a fragile solution (the longer the connection stays open the more likely it will be broken), so if the client really cares about the results then it is better to redirect as in my suggested alternative approach. – Peter Sep 11 '17 at 19:24
  • @Peter If the response is supposed to be Json, what kind of 'heartbeat' I can send. Space charactor? – Gordon Liang Sep 11 '17 at 20:29
  • Spaces and newlines *can* be used as keep-alive heartbeats inside the body of a JSON response *if* they are rendered outside string quotes. – Peter Sep 11 '17 at 20:36
  • How about the space and newlines before the header? The controllable code is within a plugin of the application, in the plugin code, the header has not been output to the stream... – Gordon Liang Sep 11 '17 at 20:57
  • The best way to figure out if keep-alives *before* the HTTP header will work for you is to try them out. But be warned that some frameworks will automatically render a "default" HTTP status 200 header as soon as the "back end" application sends the first character to STDOUT (or whatever stream, pipe, or data channel is used to send HTTP response data to the webserver). This may *not* be what you want, in which case you must render the HTTP headers first before you start generating keep-alives. – Peter Sep 11 '17 at 21:12

1 Answers1

2

Another approach for long running requests is to have the server return a 301 redirect to a URL where the request output will be published when server processing is complete. Clients may then GET this URL to check the status of the request.

This 2011 post by Thijssen describes a RESTful approach to asynchronous web requests (using 202 and 303 HTTP Status Codes) which is more elegant, but at the end of the day you'll need both the web client and server to be aware of the details; I'm not aware of any one universally supported standard for handling long running REST API calls.

Peter
  • 2,526
  • 1
  • 23
  • 32
  • Can you explain it in more detali? Looks like you are suggesting something like an async request. You mean the server keeps generating something for the redirected URL until the result is ready? – Gordon Liang Sep 11 '17 at 20:28
  • Yes. Long running API calls are usually a Bad Idea. This blog post has some more developed ideas about how to implement replacements for such (e.g. using 202 responses to indicate that a resource is being prepared but is not yet ready for access): https://farazdagi.com/2014/rest-and-long-running-jobs/ – Peter Sep 11 '17 at 20:41
  • Thanks. This would be a good approach. Unfortunately the underlying application is not under-control at all, and it requires a lot of change as well. – Gordon Liang Sep 11 '17 at 20:56
  • The premise of your question is that you're *proposing* to change the behavior of your web service, right? I don't see how you can do that unless you have some control over the underlying application... – Peter Sep 11 '17 at 21:07
  • I agree that without changing the web service behavior, it would be hard to achieve the goal. Say if we really don't want to change the service behavior, my proposed approach in the question could work. Basically I think the filter can serve the role of sending back heartbeat before the web service writes any output, and, terminate the request if the output stream has been closed. One issue here is that there is no safe way to cancel a request without application's cooperation. – Gordon Liang Sep 12 '17 at 16:17
  • So the only benefit you're hoping to get by "wrapping" the original application/service with a servlet filter is keep alives? This page indicates how you can use a filter to add a keep-alive HTTP header, but not how you can keep sending keep alive messages as long as the "back end" request is actually still running: https://stackoverflow.com/questions/24895812/how-to-force-tomcat-server-to-send-the-keep-alive-header-in-http-response (for that, the "back end" needs to communicate somehow with the "front end") – Peter Sep 13 '17 at 02:26