-
Notifications
You must be signed in to change notification settings - Fork 4
Passthroughs
--> You cannot use Orch's passthrough
directive if you also extract the request into a custom class via entity(as[...])
in the same route! <--
- Required reading: Akka Implications of the streaming nature of Request/Response Entities(you can gloss over the section on client-side handling)
- Required reading: Akka HTTP Model (specifically the difference between
HttpEntity.Strict
andHttpEntity.Default
)
Now that you've read through the above Akka docs, here's why the entity
and passthrough
directives are incompatible ...
The two entity types we are concerned with for the majority of our APIs are Strict
and Default
. The others - Chunked
, CloseDelimited
, IndefiniteLength
- concern very large request streams, such as file uploads. Strict
and Default
are used for requests with a defined size, i.e. that contain a Content-Length
request header. The overwhelming majority of our APIs handle defined-size requests; that is, the caller has fully calculated its payload before making a request to Orch.
When receiving an incoming request payload, Akka will create its RequestEntity
as Strict
if and only if the full entity is "already available in memory"; else it will create a Default
entity backed by an akka stream of bytes. The same user request, if repeated, can and will be interpreted as Strict
or Default
seemingly unpredictably. This is especially true for "large" requests, where the definition of "large" is "bordering on the size of akka's currently free buffers". In practice, these buffers grow and shrink based on other concurrent requests and akka internals. You should never assume you will be working with either a Strict
or a Default
entity.
Strict
entities, because they reside as a materialized class fully in memory, can be read by code multiple times. Default
entities, because they are a stream, can only be read once. If you attempt to read it more than once, you will get the dreaded Substream Source cannot be materialized more than once error.
Both the entity
and the passthrough
directive require the ability to read the RequestEntity
. Thus, you will get an error if you use both directives against a Default
entity ... and you have no guarantee that your entity will be Strict
or Default
.
If your route requires both entity-extraction and passthrough functionality, choose one of these approaches instead:
-
Recommended: Extract the entity using
entity(as[...])
, then call the underlying service viauserAuthedRequest
or a custom DAO, passing only the class you extracted. This delegates theStrict
vs.Default
logic to akka, which handles it internally toentity(as[...])
. Then, we never touch the request object or its entity again; we only use the class to which we extracted the entity. See https://github.com/broadinstitute/firecloud-orchestration/pull/830/files for an example of this. -
Force the entity to
Strict
via[toStrictEntity](https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/basic-directives/toStrictEntity.html)
. Wrap your route intoStrictEntity
, which forcesDefault
entities toStrict
and makes them safe to use. This has internal akka safeguards so it has some protection, but is less ideal: it brings the entire entity into memory before extracting to a custom class. Using option 1 instead allows akka to implement streamed casting in the future, even if they are not doing it now. See https://github.com/broadinstitute/firecloud-orchestration/pull/823/files for an example of this.