I have the following controller method:
#Transactional
#PostMapping(path = "/add")
#Secured({Constants.ADMIN, Constants.DATAMANAGER})
public DeferredResult<ResponseEntity> addNewCitizens(
#RequestBody List<Citizen> citizens) {
// Time out in 30 minutes
DeferredResult<ResponseEntity> output = new DeferredResult<>((long) (1000 * 60 * 30));
ForkJoinPool.commonPool().submit(() -> {
// Long operation...
output.setResult(new ResponseEntity(HttpStatus.OK));
}
return output;
}
The method usually receives a lot of data and uses it to update my database. For the data I'm testing with right now it takes 3-4 minutes.
For some reason, when it takes a while for it to process the request, it starts all over when it has finished running once, as if it received another request. The Angular front end application from which i sent the request never receives a response in this case. For fast requests it works perfectly. Also, I checked the network tab in Chrome and I can only see one request getting sent to the endpoint in there.
What could be causing this, and what can I do to fix it? Thanks!
Additional info: It only happens when I deploy the service to a remote server - not when I test on my local machine...
Related
In my application.properties file I have...
server.port=8086
server.connection-timeout=15000
I know that the file is being loaded correctly because the server is running on port 8086.
In the application I have a RestController
#RestController
class TestController {
#GetMapping()
fun getValues(): ResponseEntity<*> {
return someLongRunningProcessPossiblyHanging()
}
}
When I call the endpoint, the request never times out, it just hangs indefinitely.
Am I missing something?
NOTE: I've also been informed that Tomcat uses this field in minutes, not milliseconds (rather unusual choice IMO). I've tried setting this to server.connection-timeout=1 denoting 1 minute, but this didn't work either.
NOTE: I don't want another HTTP request to cause the previous request to time out, I want each HTTP request to timeout of it's own accord, should too much time elapse to serve the request.
connection-timeout does not apply to long running requests. It does apply to the initial connection, when the server waits for the client to say something.
Tomcat docs (not Spring Boot) define it as The number of milliseconds this Connector will wait, after accepting a connection, for the request URI line to be presented [...]
To test the setting server.connection-timeout=4000 I connect using netcat and I don't send any HTTP request/headers. I get:
$ time nc -vv localhost 1234
Connection to localhost 1234 port [tcp/*] succeeded!
real 0m4.015s
user 0m0.000s
sys 0m0.000s
Alternatives
1) Async
From brightinventions.pl - Spring MVC Thread Pool Timeouts:
In Spring MVC there is no way to configure a timeout unless you use async method. With async method one can use spring.mvc.async.request-timeout= to set amount of time (in milliseconds) before asynchronous request handling times out.
I've set spring.mvc.async.request-timeout=4000 and I get a timeout in the browser with this:
#GetMapping("/test-async")
public Callable<String> getFoobar() {
return () -> {
Thread.sleep(12000); //this will cause a timeout
return "foobar";
};
}
See Spring Boot REST API - request timeout?
2) Servlet filter
Another solution would be to use a servlet filter brightinventions.pl - Request timeouts in Spring MVC (Kotlin):
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
val completed = AtomicBoolean(false)
val requestHandlingThread = Thread.currentThread()
val timeout = timeoutsPool.schedule({
if (completed.compareAndSet(false, true)) {
requestHandlingThread.interrupt()
}
}, 5, TimeUnit.SECONDS)
try {
filterChain.doFilter(request, response)
timeout.cancel(false)
} finally {
completed.set(true)
}
}
3) Tomcat Stuck Thread Detection Valve?
Tomcat has a Stuck Thread Detection Valve but I don't know if this can be configured programmatically using Spring Boot.
From the official docs:
server.connection-timeout= # Time that connectors wait for another HTTP request before closing the connection. When not set, the connector's container-specific default is used. Use a value of -1 to indicate no (that is, an infinite) timeout.
Another ref, also mentions the same. It should work for you.
When I call the endpoint, the request never times out, it just hangs indefinitely.
server.connection-timeout isn't a request timeout. It is a timeout for idle connections, i.e. those that have already had a request/response pair and on which the server is now awaiting a second request. It is essentially a server-side read timeout.
In my application I need to implement functionality which ensure that if client makes GET request, application will hold this request until some change happen in database and also be possible to set maximal holding time.
For example:
User makes GET request and request will hold for 20 seconds. If during these 20 s something changes in database, application release this request with required data, else application hold request for 20s.
I decide to use long polling. In my application I am using Spring Boot as well. Can you tell me if it possible do it with Spring or should I add some another library for that?
I also found Spring Scheluder for holding request for some interval, but problem is that, scheluder is not allowed for methods with parameters, but I need fetch data by specific user, so at least user's id should be passed. Also I am not sure if it possible to manually release this scheluder when it is needed.
Thanks for advice!
For long pulling request you can use DeferredResult. when you return DeferredResult response, request thread will be free and this request handle by worker thread. Here is one example:
#GetMapping("/test")
DeferredResult<String> test(){
Long timeOutInMilliSec = 100000L;
String timeOutResp = "Time Out.";
DeferredResult<String> deferredResult = new DeferredResult<>(timeOutInMilliSec,timeOutResp);
CompletableFuture.runAsync(()->{
try {
//Long pooling task;If task is not completed within 100 sec timeout response retrun for this request
TimeUnit.SECONDS.sleep(10);
//set result after completing task to return response to client
deferredResult.setResult("Task Finished");
}catch (Exception ex){
}
});
return deferredResult;
}
In this request give response after waiting 10 sec. if you wait more than 100 sec you will get timeout response.
Look at this.
Situation:
I have a Java (Spring) web application, where a user can upload a file. This file will then be analyzed and the results are presented to the user on a new JSP.
The simplified controller method looks like this:
#RequestMapping(method = RequestMethod.POST)
public String fileUploaded(Model model, File file, BindingResult result, HttpServletRequest request, HttpServletResponse response) {
/** Do very long calculations. */
model.addAttribute("map", /** very large map */);
return "result_page";
}
Problem:
The code works fine on my local Netbeans/Tomcat configuration. But on the server (after deploying the *.war), the application does not work for large files. The program runs till the return "result_page"; statement and then the page is not displayed. Without any error in the tomcat Log (catalina.out). The previous upload-JSP remains present. Note, that the large files are working on my local Tomcat.
Tried Solutions:
I thought the problem could be the session-timeout. On my local config, the large files took 20 minutes until the return is reached. On the server, it takes 40 minutes. Thus, the default 30 minutes timeout could be an issue. I tried to change the timeout in the web.xml. But setting the session-timeout to 120 minutes did not solve the issue. Also setting the timeout to 1 minute didn't reproduce the error on my local configuration.
Question:
What am I missing? Is it the timeout I just configured wrong, or what could be the reason? Are there other Log-Files I can search for Error-Messages?
I've been doing this tutorial about how to return async callable object. It works as intended. But while the first request sleeps for 5 seconds I get the second request, controller waits for the previous request to finnish before handling the second one.
How to make controller handle immediately every request and make sleeping in a background?
#Edit
Example:
Imagine a situation, that my controller needs to make a request to external api and based on its response it should send his own response. External api call takes lets say 2 seconds. I want users of my application to wait only that 2,5 seconds and not be placed in queue, because the controller can handle only one request at a time.
Is REST controller multithreaded?
REST controller is multithreaded as the DisptcherServlet handles multiple requests from the clients concurrently and serves using the respective controller methods. You can refer the request handling flow here
How to make controller handle immediately every request and make
sleeping in a background?
You can do that by returning Callable<String> in the Spring controller method as shown below:
#Controller
public class MyController {
#RequestMapping(value="/sleep")
public Callable<String> myControllerMethod() {
Callable<String> asyncTask = () -> { try {
System.out.println(" WAITING STARTED:"+new Date());
Thread.sleep(5000);
System.out.println(" WAITING COMPLETED:"+new Date());
return "Return";//Send the result back to View Return.jsp
} catch(InterruptedException iexe) {
//log exception
return "ReturnFail";
}};
return asyncTask;
}
Output:
WAITING STARTED: Thu Nov 24 21:03:12 GMT 2016
WAITING COMPLETED: Thu Nov 24 21:03:17 GMT 2016
After this, the view will be returned "Return.jsp" page.
Here, the controller method will be running in a separate thread (releasing the actual servlet thread) and once the task is completed the Result will be sent back again to the client (View etc..).
P.S.: You need to add #EnableAsync as part of your application configuration, you can look here on this.
What you want to do is what it is supposed to be done in the first example of the tutorial you linked:
#RequestMapping(value = "/block", method = RequestMethod.GET, produces = "text/html")
public String executeSlowTask() {
logger.info("Request received");
Thread.sleep(5000);
logger.info("Servlet thread released");
return result;
}
This blocks the calling thread and waits till it is done. If you call from a different HTTP session, it will be another thread, so it will be a new 5 seconds wait (not affected by the first one).
Threads are linked to HTTP calls as long as there are threads available in the pool (configuration of the tomcat server).
It is not the case that the controller will block all subsequent calls while busy. It is multithreaded.
In an Async servlet processing scenario, I want to achieve cancellation of requests.
(Am also hoping to keep this RESTful)
Say, I have a code like this:
#RequestMapping("/quotes")
#ResponseBody
public void quotes() {
//...
final AsyncContext ac = request.startAsync();
ac.setTimeout(0);
RunJob job = new RunJob(ac);
asyncContexts.add(job);
pool.submit(job);
};
// In some other application-managed thread with a message-driven bean:
public void onMessage(Message msg) {
//...
if (notEndOfResponse) {
ServletOutputStream out = ac.getResponse().getOutputStream();
//...
out.print(message);
} else {
ac.complete();
asyncContexts.remove(ac);
}
};
If the Client decides to cancel this processing at the server-side, it needs to send another HTTP request that identifies the previous request and the server then cancels the previous request (i.e stops server-side processing for that request and completes the response for it).
Is there a standard way to do this ?
If it is the case that there is NO standard way to do this and each developer does it as per their will and skill, I would like to know if my (trivial) approach to this problem is ok.
My way (after #Pace's suggestion) is:
Create a "requestId" on the server and return a URL/link as
part of the first partial responses (because I could get
many partial responses for a single request as part of Async processing).
The link could be, for ex:
.../outstandingRequests/requestId
When needing to cancel the request, the client does a DELETE request on the URL and let the server figure out how to achieve cancellation at its end.
Any problems with this approach ?
When using long running operations/tasks in a RESTful sense it is best to treat the operation itself as a resource. A post to the operations URL returns a URL you can use to GET the status of that operation (including the results when the operation finishes) and a DELETE to that URL will terminate the operation.