In the end, the only thing that really completes the request is the call to requestContext.complete . Thus, it does not matter which thread or context of the actor this call is made from. All that matters is that this happens during a given period of waiting for a request. You can. Of course, this question will call itself one way or another, but the spray gives you a few predefined constructs that might be better for yours than passing the actual RequestContext. Mainly:
- The
complete directive, which simply provides some sugar on top of the "raw" < ctx => ctx.complete(โฆ) . - The future marshaller that calls
ctx.complete from the future.onComplete handler. - The
produce directive, which extracts the function T => Unit , which can subsequently be used to complete the request with an instance of a custom type.
Architecturally, in most cases, itโs nice to have an API layer โleakโ into the core of your application. That is, the application should not know anything about the API or HTTP level. It must deal with objects of its own domain model. Therefore, RequestContext directly in the application core is basically not the best solution.
By resorting to "demand" and relying on the future of Marshaller, an obvious, well-understood and fairly simple alternative. It comes with a (small) flaw, which is requested with a mandatory timeout check by itself, which is not logically required (since the spray layer already executes the request timeouts). A timeout on request is required for technical reasons (therefore, the underlying PromiseActorRef may be cleared if the expected response never arrives).
Another alternative to passing RequestContext is produce (for example, produce(instanceOf[Foo]) { completer => โฆ ). It extracts a function that you can pass to the kernel application. When your main logic calls complete(foo) completion logic is executed and the request is complete. Thus, the application core remains separated from the API level, and the overhead is minimal. The disadvantages of this approach are twofold: at first, the complementary function is not serializable, so you cannot use this approach in the JVM boundary. And secondly, the completion logic is now executed directly in the context of the application core actor, which may change behavior during operation in an undesirable way, if it is necessary for Marshaller [Foo] non-trivial tasks.
The third alternative is to spawn an actor for each request in the API level and process the response returned from the application core. Then you do not need to use the request. However, you find yourself in the same problem in that the PromiseActorRef underlying the request has: how to clear if no response is ever returned from the application kernel? With a second request, you have complete freedom to implement a solution to this issue. However, if you decide to rely on a timeout (for example, through context.setReceiveTimeout ), there may be no advantage over โdemandโ.
Which of the described solutions is best suited for your architecture is up to you to decide. However, as I hope was able to show, you are doing there are several alternatives to choose from.
To answer some of your specific questions: there is only one actor / handler that serves the route this way, if you block it, the spray will be blocked. This means that you want to either complete the route immediately or send the job using one of the three options above.
There are many examples on the Internet for these 3 options. The easiest way is to wrap your code in Future . Check also the "actor per request" / example. Ultimately, your architecture will determine the most appropriate way.
Finally, Spray runs on top of Akka, so all of the Akka settings are still applied. See HOCON reference.conf and application.conf for Actor streaming settings.