Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing request scoped beans in a multi-threaded web application

Scenario: We have a Spring managed web application that runs inside Websphere. (Spring 3.0.x, WAS 7) The webapp leverages Websphere's work manager via Spring's WorkManagerTaskExecutor (configured with a thread pool size of 10) to execute computational intensive db read operations. So basically, a request comes in to generate, lets say, 10 different documents. To generate the documents only db reads are needed to gather/process the data. So we basically spawn 10 threads to process the 10 documents and at the end gather the 10 documents returned from the 10 workers and merge them and write back one big response to the client. What we identified is that while the 10 threads are gathering/processing the data there are bunch of similar db calls made. So what we came up with is to create an Aspect around the most-executed db methods to cache the response. The aspect is configured as a singleton, and the cache the aspect uses is autowired into the aspect with a scope set to request-scope so that each request has its own cache.

Problem: Now the problem with this approach is that when the threads are doing their db calls and the Aspect is interjects we are getting java.lang.IllegalStateException: No thread-bound request found exception. Which I understand is totally valid as the threads are being executed outside the request context.

Is there a way to circum-navigate this issue? Is it possible to apply the aspect with a request scoped cache to the methods invoked by these threads?

like image 806
r4j1v Avatar asked Oct 03 '11 16:10

r4j1v


1 Answers

I don't think you can do this directly. Even if you could, it would be a bit ugly. However, you can generate a unique request identifier (or even - use the session id, but careful with multiple tabs), and pass that to each processing thread. Then the aspect can use that id as the key to the cache. The cache itself will also be singleton, but there will be Map<String, X>, where String is the ID and X is your cached result.

To make things easier to handle, you can have @Async methods (rather than manually spawning threads), and each @Async method can have the cache id passed as its first parameter.

(Of course, your asynchronous methods should return Future<Result> so that you can collect their results in the request thread)

like image 71
Bozho Avatar answered Oct 19 '22 21:10

Bozho