Skip to content

docs(054): make the ready set part of the design - #273

Merged
lxsaah merged 1 commit into
mainfrom
docs/054-ready-set
Oct 3, 2026
Merged

lxsaah merged 1 commit into
mainfrom
docs/054-ready-set

Conversation

@lxsaah

@lxsaah lxsaah commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Description

Design 054 as merged in #272 specifies that the connector's single transport task polls every outbound route on each wake-up. A per-route waker set (the "ready set") appeared only as an open question in §8. A second prototype round measured that design the way a connector actually runs it, with the producer and the transport in separate Tokio tasks. Polling every route then costs about 120 ns per idle route per message, which makes it slower than today's design almost immediately:

Routes (one busy) Task per route (main today) One task, poll every route (054 as merged) One task, ready set (this PR)
1 478 ns 450 ns 622 ns
8 467 ns 1,273 ns 615 ns
64 454 ns 7,916 ns 590 ns
256 489 ns 32,230 ns 631 ns

Median of 5 runs. All three were measured on the same host and tree.

The ready set gives each route its own waker; waking it queues the route and wakes the transport task, so a wake-up only polls the routes that woke. It stays flat with zero allocations per message, parked or not, and passes the same 12 semantics tests as the scanning version: fairness, select safety, and no lost wake-up after a skipped value. This PR moves it into the design. The doc changes:

  • §4.2: poll_stage takes routes from the ready set, oldest first. The section gives the ReadySet / RouteWake shape and the ordering rules:

    • register the task's waker before reading the queue;
    • put a route back at the end of the queue after it stages a value;
    • return Ready(None) after the last route closes.

    The measured cost replaces the old 80 ns-per-route figure, which came from a single-task loop that never parked.

  • §4.6: fairness is oldest-woken first. The outage behaviour change is limited to connectors that own their send path. Native MQTT still queues inside rumqttc's request channel: in the outage test it sent all ten stale values both before and after this change, where the embedded backend now sends only the latest.

  • §5 / §5.1:

    • ready-set rows measured at 0 allocations;
    • an informational (non-gating) timing bench, so a regression back to scanning shows up;
    • 5-run latency, 56.1 → 44.4 µs median;
    • live heap: +4.3 KB held up front, +0 B while traffic flows;
    • the outage and wake-up tables.
  • §7: polling every route is recorded as the rejected previous revision.

  • §8: the "many routes" question is resolved on the host. The ready set's cost on the STM32H5 board and with Embassy's buffers stays open.

These changes keep the design consistent with the write-up on the costs and benefits of removing allocations, which uses AimDB as its worked example.

Related Issue

Checklist

  • I have read the CONTRIBUTING.md document.
  • My code follows the project's coding standards. (Docs only.)
  • I have added tests to cover my changes. (Not applicable: docs only. The prototype's tests and benches live on an unpublished throwaway branch.)
  • All new and existing tests passed (make check). (Not run for this docs-only change.)
  • I have updated the documentation accordingly.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HXDnQEx2THUSTASYkThxCw


Generated by Claude Code

A second prototype round measured the outbound pull with the producer and
the transport in separate tasks, as they run in a connector. Polling every
open route then costs about 120 ns per idle route per message: 1,273 ns at
8 routes and 32,230 ns at 256, against about 470 ns for today's task per
route. A ready set fed by per-route wakers stays flat at 590-630 ns with
zero allocations, so it moves from an open question into §4.2.

- §4.2: poll_stage takes routes from the ready set, oldest first; the
  ReadySet/RouteWake shape, wake ordering (register the task waker before
  reading the queue), requeue after a staged value, Ready(None) after the
  last closed route; measured cost.
- §4.6: fairness is oldest-first over routes that woke. The outage change
  holds where the connector owns its send path; native MQTT still queues
  in rumqttc's request channel (sent all ten values before and after).
- §5/§5.1: ready-set rows measured at 0 allocations; an informational
  scan-cost bench beside the gate; 5-run latency, live heap and outage
  results; the two-task wake-up table.
- §7: polling every route recorded as the rejected previous revision.
- §8: the many-routes question is resolved on the host; the ready set's
  cost on the STM32H5 rig and Embassy buffers stays open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXDnQEx2THUSTASYkThxCw
@lxsaah
lxsaah merged commit 137ad2d into main Oct 3, 2026
6 checks passed
@lxsaah
lxsaah deleted the docs/054-ready-set branch October 3, 2026 08:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants