Clarification on handling of HTTP calls, multi-threading and dependency injection


#1

EDIT: After some more experimenting, I got the tracing to work with GlobalTracer.

I had to reformat the code somewhat. The use of a separate execution context for database calls made the calls lose their tracing scope context, apparently.

Jaeger Tracing uses a ThreadLocal scope, which would explain that(I think).

=============

Original post:

I’ve always assumed this, but was never certain.

When play receives an HTTP call, is this handled within its own thread? As in, each call is its own thread?

Reason I’m asking is because I’m wondering about Dependency Injection acts singleton… but per thread.

So not Singleton in the sense of “one instance for the entire application, shared across all threads”, but “one instance per thread”.

Which is related to trying to get tracing to work: https://github.com/KoenDG/play-java-ebean-example/pull/1

The main problem being that either you have a singleton which isn’t what I need(I think), or the dependency injection creates a new object every time it’s injected, which also isn’t what I need.

The main solution I can think of now is to put it in the ThreadLocal Context and request it where I need it.


(Marcos Pereira) #2

Hey @KoenDG,

Looks like you have everything figured out, right? Some comments about your questions:

Play uses a thread pool that has a size limit. The idea is that since Play uses non-blocking IO you can have a smaller thread pool. That means threads are reused and Play does not create a new thread for each call. That also means you don’t want to block the threads in the default thread pool because this will leads to bad performance and poor use of resources.

What you do eventually is that if you need to do some blocking operation, you create another thread pool that will be used to run these operations instead of using the default one. Then, when moving between these different threads, you need a way to propagate the state.

If it is a @Singleton, then it will be a single instance per application. Guice will handle that for you.

Best.


(Marcos Pereira) #3

@KoenDG,

To do some clarification on the singleton aspects. I’m seeing the code you linked here: Tracing, part 2. More specifically, I’m looking at this. So, in Java, when composing actions (extends Action.Simple, for example) the action composition won’t be a singleton. Even if controllers are singletons, and I think that even if you annotate BeforeControllerAction with @Singleton, Play will create a new instance for each call.

But that has nothing to do with the thread pool.

A possible workaround is to use a filter and routes modifier tags if you don’t want to trace all routes. You will end up with routes like:

+ trace
POST /api/foo/bar ApiController.foobar

And in your filter, you can inspect request.attrs() and only start a trace if the attribute is present.

Best.


#4

@marcospereira Thanks for the pointers.

So, there’s the thread pool and threads from that pool get re-used… but what I’m really wondering is: each such call being handled in a thread is thread-safe, right? As in, commands from inside 1 thread cannot influence other threads directly? Everything I know tells me it’s the case, but why not ask to be sure, I’m thinking.

As for the tracing itself and the need to only have 1 Tracer, I eventually got things working by using GlobalTracer and also by doing this.

I further found that each Tracer instance will attempt to claim a socket for UDP transport upon initialization. Creating new Tracers all the time eventually locks everything up, as there will be no more UDP sockets to get.

I did not know about those routes modifier tags, thanks for linking that, that could turn out to be really useful.