Reputation: 24679
I have the following Spring MVC 3.2 code (it uses the DeferredResult class):
@RequestMapping(value = "getMessages", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public DeferredResult<List<Message>> getMessages(@RequestParam final Long senderId) {
final Long recipientId = memberService.retrieveCurrentMember().getId();
final String messageRequestKey = new StringBuilder().append(senderId).append(":").append(recipientId).toString();
final DeferredResult<List<Message>> deferredResult = new DeferredResult<List<Message>>(null, Collections.emptyList());
messageRequests.put(messageRequestKey, deferredResult);
deferredResult.onCompletion(new Runnable() {
@Override
public void run() {
messageRequests.remove(messageRequestKey);
}
});
List<Message> unReadMessages = messageService.findUnreadMessages(senderId, recipientId);
if (!unReadMessages.isEmpty()) {
deferredResult.setResult(unReadMessages);
}
return deferredResult;
}
This method is polled continuously by an ajax call and it systematically causes Tomcat to crash upon the 9th method invocation. Note that Tomcat crashes without any error message.
I would be very grateful if someone could help me determine the reason why this code exhibits this behavior, perhaps by giving me tips on how to debug the app/tomcat.
Upvotes: 1
Views: 1350
Reputation: 2841
I tried your modified mvc-chat code.
with it, a GET request was issued as soon as I signed-in and since the timeout was 3sec, it timedout and the GET response came with an empty list of messages. The client immediately issued another GET request in response to this, and the second GET hung inside "myService.someTransactionalMethod();" in the call to "messageRepository.findAll();" !
If I increase the timeout and let the GET request 'complete', then a subsequent GET request is able to call the Tx-enabled method properly. But if a request 'timedout', a subsequent GET request (from the same chat client) is blocked in the Tx-enabled method. (i.e a problem with clean-up).
Similarly, when a GET is outstanding and a parallel GET is issued (say, from another browser window) the second GET gets blocked in the Tx-enabled method.
This seems a problem with the DeferredResult's interaction with OEMIV filter's semantics. You have done the right thing by raising this in the Spring forum !
Edit1: To confirm the above claim, I removed the OEMIV filter from xml (and left ALL other code, including the Tx method and the DeferredResult, unchanged) and it worked without any issues.
Edit2: Rossen Stoyanchev commented on the Spring forum that :
"On the initial request thread, the OEMIV filter acquires an EM instance, then the
controller is invoked, which in turn invokes the tx method. The tx method re-uses the
same EM instance. Then the controller method returns and the Servlet container thread
is exited, but OEMIV filter doesn't release the EM instance because it is aware of the
async processing.
When the DeferredResult is set, a dispatch is made back to the container, hitting the
OEMIV filter and the DispatcherServlet once again. When that thread completes, OEMIV
filter will release the EM instance. Note that this is true even if the DeferredResult
is set in the controller method.".
From the above, it looks as if, when the async request times out, the OEMIV filter is either not getting involved and even if it does get involved it does not get a chance to release the EM instance that it holds.
Upvotes: 1