Skip to content

module ActionController::ExecutionContext #

Manages the optional response execution context(s).

When execution contexts are enabled (see ENABLED) 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.

Constants#

ENABLED = true#

Class methods#

.parallelism(name : String, size : Int32) : Int32#

override the parallelism of a named context before it is first used (e.g. to size it from an environment variable). Has no effect once the context has been created.

View source

.response_context : ::Fiber::ExecutionContext::Parallel#

the default execution context response bodies are serialised in

View source

.response_parallelism : Int32#

the number of parallel schedulers used to serialise response bodies. must be configured before the server starts as the context is built lazily.

View source

.response_parallelism=(size : Int32) : Int32#

configure the number of parallel schedulers used to serialise responses

View source

Macros#

define(name, parallelism = 4)#

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). When execution contexts are disabled this only records the name (so routes referencing it still validate) and creates no context.

View source

offload(context = nil#

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.

When execution contexts are disabled the block simply runs inline.

View source

offload_responses(enabled = true)#

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 when execution contexts are disabled.

ActionController::ExecutionContext.offload_responses
View source