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.cr

Class Method Summary

Macro Summary

Class Method Detail

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

no-op: execution contexts are not enabled


[View source]
def self.response_parallelism=(size : Int32) : Int32 #

no-op: execution contexts are not enabled


[View source]

Macro Detail

macro 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). Without the execution context compilation flags this only records the name (so routes referencing it still validate) and creates no context.


[View source]
macro offload(context = nil, &block) #

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.


[View source]
macro 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 without the execution context compilation flags.

ActionController::ExecutionContext.offload_responses

[View source]