module
ActionController::ExecutionContext
Overview
Manages the optional response execution context(s).
When compiled with -Dpreview_mt -Dexecution_context the response body
serialisation (typically converting a handler's return value into JSON) runs
in a dedicated parallel execution context. Moving the serialisation off the
request fiber means a large, CPU heavy JSON response is serialised in
parallel and cannot cause head-of-line blocking of other requests.
Applications define named contexts and bind them to a controller
(execution_context "name") or an individual route
(@[AC::Route::GET("/", execution_context: "name")]) so heavy endpoints get
their own dedicated pool - the whole request runs there. The binding is
resolved at compile time - the generated route code calls the chosen context's
getter directly, there is no per-request lookup. Named contexts are created
once, lazily, on first use.
Routes that are not bound to a context serialise their response inline on
the request fiber by default. Call offload_responses to instead offload
those responses to the shared response_context (useful when unbound routes
can return large payloads).
# configure your contexts (e.g. in config.cr), names are string literals
ActionController::ExecutionContext.define "reports", parallelism: 2
ActionController::ExecutionContext.define "heavy-json" # default size
When execution contexts are not enabled the configuration is a no-op and
serialize_response simply yields inline, so third party code referencing
this module still compiles unchanged.
Defined in:
action-controller/execution_context.crClass Method Summary
-
.parallelism(name : String, size : Int32) : Int32
no-op: execution contexts are not enabled
-
.response_parallelism=(size : Int32) : Int32
no-op: execution contexts are not enabled
Macro Summary
-
define(name, parallelism = 4)
define a named execution context, created once and lazily on first use.
-
offload(context = nil, &block)
run a block in an execution context (defaulting to the shared
response_context), blocking the calling fiber until it completes. -
offload_responses(enabled = true)
enable offloading of responses to the shared
response_contextfor routes and controllers that are not bound to a dedicated execution context.
Class Method Detail
no-op: execution contexts are not enabled
Macro Detail
define a named execution context, created once and lazily on first use.
ActionController::ExecutionContext.define "reports", parallelism: 2
The size can be overridden at runtime before the context is first used via
ActionController::ExecutionContext.parallelism("reports", n). Without the
execution context compilation flags this only records the name (so routes
referencing it still validate) and creates no context.
run a block in an execution context (defaulting to the shared
response_context), blocking the calling fiber until it completes.
Used both to offload just the response serialisation (default context) and to run a whole request in a controller/route bound context. The block is inlined into the fiber spawned in the chosen context - the calling fiber blocks on a channel until it completes, freeing it to handle other requests while the potentially CPU heavy work runs in parallel. Any error is propagated back to the calling fiber. Implemented as a macro so the body is not captured as a separate closure.
Without the execution context compilation flags the block simply runs inline.
enable offloading of responses to the shared response_context for routes
and controllers that are not bound to a dedicated execution context.
Off by default: by default an unbound response is serialised inline on the request fiber (no spawn, no channel) which is cheaper for small responses. Enable it when your unbound routes can return large, CPU heavy payloads that would otherwise cause head-of-line blocking.
This is a compile-time switch - call it before your controllers are finalised (e.g. in config.cr). It has no effect without the execution context compilation flags.
ActionController::ExecutionContext.offload_responses