Actor Continuation Passing: Efficient and Extensible Request Routing for Event-Driven Architectures
The logic for handling of application requests to a staged, event-driven architecture is often distributed over different portions of the source code. This complicates changing and understanding the flow of events in the system. The article presents an approach that extracts request handling logic from regular stage functionality into a set of request scripts. These scripts are executed step-wise by sending continuations that encapsulate their request’s current execution state to stages for local processing and optional forwarding of follow-up continuations. A new internal domain specific language (DSL) that aims to simplify writing of request scripts is described along with its implementation for the scala actors library. Evaluation results indicate that request handling with actor continuations performs about equally or better compared to using separate stages for request handling logic for scripts of at least 3 sequential steps.
💡 Research Summary
The paper addresses a common difficulty in staged, event‑driven architectures: request‑handling logic is interleaved with stage‑specific code, making the overall flow hard to understand, modify, and extend. Traditional solutions introduce a coordinator stage that centralises request logic, but this adds extra messages, context switches, and often requires new event types to carry intermediate data.
To overcome these drawbacks, the authors propose Actor Continuation Passing (ACP). In ACP, the current execution state of a request is captured as a continuation—a first‑class anonymous function that contains the stack frame and any local variables. This continuation is sent as a single message to the next stage, where it is invoked with the stage’s “prop” (the object exposing the stage’s functionality). Because the continuation already carries all intermediate values, no additional event payloads are needed. The request therefore proceeds step‑by‑step across stages without an explicit coordinator, and without nested callbacks.
The implementation, named Souffleuse, is built on the Scala actors library. It provides a domain‑specific language (DSL) that lets developers write request scripts using familiar constructs:
remember(value)– bind a value for later reuse.compute(thunk)– execute a thunk on the current stage and bind its result.goto(stage)– capture the current continuation and transfer execution to another stage, returning a handle to that stage’s prop.yield(result)– optionally return a final result to the script’s caller.
Scripts are expressed as Scala for‑comprehensions, which the Scala compiler automatically rewrites into continuation‑passing style (CPS). Each generator’s flatMap and map methods receive the remaining computation as a continuation function. Souffleuse supplies a custom Responder that implements flatMap/map by simply sending the continuation to the target actor (self.send(k)). Consequently, goto becomes a one‑liner that captures the continuation and forwards it, while compute and remember are built on top of this primitive.
Two execution primitives are offered: run (synchronous, blocks the calling actor until the script yields) and asyncRun (non‑blocking). The DSL can also be used by subclassing the abstract Play class, allowing static type checking of the stages involved.
Performance is evaluated using a “ring of stages” benchmark where a request circulates through a configurable number of stages. The authors compare ACP against the traditional coordinator‑stage approach (named Ping‑Pong in the figures). Results on an 8‑core machine show that for rings of three or more stages, ACP matches or outperforms the coordinator method, with up to a two‑fold reduction in average latency. The reduction stems from halving the number of messages: each hop now carries a continuation instead of a request‑response pair. The overhead of serialising and deserialising continuations is shown to be negligible.
Limitations are openly discussed. The current DSL assumes a linear flow; non‑linear control structures (conditionals, loops, parallel branches) require either nested sub‑scripts or additional coordination logic. Exception handling across stage boundaries is not supported, as the authors consider the semantics non‑trivial, especially for non‑linear scripts that may involve synchronisation points. Future work includes extending the language with proper exception propagation, richer synchronisation primitives, and mechanisms for re‑using or load‑balancing continuations across multiple actors.
In conclusion, Actor Continuation Passing provides a clean separation between request‑level and stage‑level concerns without incurring the performance penalties of explicit coordinator stages. By leveraging Scala’s built‑in CPS transformation of for‑comprehensions, Souffleuse offers a concise, readable DSL that eliminates deeply nested callbacks while preserving high throughput in staged, event‑driven systems.
Comments & Academic Discussion
Loading comments...
Leave a Comment