ruststream 0.7.4

Async messaging framework for Rust: broker-agnostic traits, router, codecs, and a conformance harness for broker authors.
Documentation
---
title: Async
description: "The tasks a running service holds, the way one delivery passes through them, and the order a shutdown stops them in."
---

import {Diagram} from '@site/src/components/Diagrams';

export const topology = {
  "title": "The tasks of each dispatch form on the app runtime, and the dedicated threads beside it",
  "text": "The diagram as text",
  "broker": "Subscription streams of the connected brokers",
  "runtime": "App runtime: Tokio, multi-thread by default",
  "loop": ["default", "one task runs", "every step"],
  "pool": ["workers(n)", "one loop task", "hands them out"],
  "feeder": ["threads(n)", "one feeder task", "fills the rings"],
  "workerTasks": ["n worker tasks", "an inbox of one"],
  "rings": ["n rings", "of eight slots"],
  "tracker": ["Tracked tasks: and_after,", "hooks, retry_after timers"],
  "osThreads": ["n OS threads", "a Tokio runtime", "on each"],
  "poll": "poll_next",
  "inbox": "send",
  "push": "push",
  "pop": "pop",
  "spawn": "spawn"
};

export const timeline = {
  "title": "One delivery, step by step, in the column of the task that runs each step",
  "text": "The diagram as text",
  "loopLane": "Loop task",
  "handlerLane": "Handling task",
  "laterLane": "Later, tracked",
  "pollStep": ["poll the", "stream"],
  "context": ["build the", "context"],
  "layers": ["consume", "layers"],
  "decode": "decode",
  "body": ["handler", "body"],
  "reply": ["publish", "the reply"],
  "settle": "settle",
  "next": ["poll the", "next one"],
  "nextTurn": "next turn",
  "after": ["continuations", "and timers"],
  "spawn": "spawn"
};

export const shutdown = {
  "title": "What a graceful shutdown does, in order",
  "text": "The diagram as text",
  "stop": ["A signal, a fail-fast", "or RunningApp::shutdown"],
  "onShutdown": ["on_shutdown hooks run", "while the brokers are connected"],
  "cancel": ["The loops stop", "taking new deliveries"],
  "finish": ["Deliveries in hand", "are handled and settled"],
  "drain": ["Tracked continuations", "and retry timers finish"],
  "brokers": ["Brokers shut down", "in reverse registration order"],
  "afterShutdown": ["after_shutdown hooks run", "with the brokers closed"]
};

# Async

The tasks a running service holds, the way one delivery passes through them, and the order a
shutdown stops them in.

<Diagram name="asyncTopology" words={topology} />

The service runs on Tokio. [`#[ruststream::app]`](../components/application/app-macro.md) builds a
multi-thread runtime. `flavor = "current_thread"` picks a single-threaded one, and
`worker_threads = n` sets the number of threads.

A subscriber is a `Stream` of deliveries, and back-pressure comes from polling it. The default
loop polls for the next delivery once the previous one is settled.
[`workers(n)`](../components/subscribers/dispatch/workers.md) keeps at most `n` deliveries in
flight. `workers(n, by_key)` sends every delivery of one key to the same worker, so one key keeps
its order. [`threads(n)`](../components/subscribers/dispatch/threads.md) reads ahead at most
`n × 9` deliveries: one in the hands of each thread and eight in its ring.

A dedicated thread runs the whole delivery, from decode to settlement, on its own current-thread
runtime. Work the handler hands to [`MainRuntime`](api:ruststream::runtime::MainRuntime), taken as
[`Ctx<MainRuntime>`](../components/subscribers/context/ctx.md), runs back on the app runtime.

## One delivery

<Diagram name="asyncTimeline" words={timeline} />

The handling task is the loop task itself by default, a worker task under `workers(n)`, and a
dedicated thread under `threads(n)`. An [`and_after`](../components/subscribers/messages/and-after.md)
continuation, a post-settle hook and a [`retry_after`](../components/subscribers/messages/retry-after.md)
timer run after the settlement, as tasks the app tracks. They run at most once: the delivery is
already settled.

## Shutdown

<Diagram name="asyncShutdown" words={shutdown} />

A [shutdown timeout](../components/application/running/shutdown.md) bounds the wait. When it
expires, loops still running are aborted, and tracked tasks still pending are left to finish on
their own. Without a timeout, a shutdown waits for all of them. A task spawned with plain
`tokio::spawn` on a dedicated thread is not tracked and ends with its thread.