Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions docs/asynchronous-outbound-messaging-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,54 @@ loop {
}
```

#### 3.2.1 Fairness for low-priority frames

Continuous bursts of urgent messages can prevent the low-priority queue from
ever being drained. To mitigate this without removing the deterministic bias,
each `ConnectionActor` tracks how many high-priority frames have been processed
in a row. After a configurable threshold (`max_high_before_low`), the actor
checks `low_priority_push_rx.try_recv()` and, if a frame is present, processes
it and resets the counter.

An optional time slice (for example 100 µs) can also be configured. When the
elapsed time spent handling high-priority frames exceeds this slice, and the low
queue is not empty, the actor yields to a low-priority frame. Application
builders expose `with_fairness(FairnessConfig)` where `FairnessConfig` groups
the counter threshold and an optional `time_slice`. The counter defaults to 16
while `time_slice` is disabled. Setting the counter to zero preserves the
original strict ordering.

This fairness mechanism ensures low-priority traffic continues to progress even
under sustained high-priority load.

<!-- markdownlint-disable MD033 -->

The flow diagram below summarises the fairness logic.

<description>The diagram shows how the actor yields to the low-priority queue
after N high-priority frames.</description>

<!-- markdownlint-enable MD033 -->

```mermaid
flowchart TD
A[Start select! loop] --> B{High-priority frame available?}
B -- Yes --> C[Process high-priority frame]
C --> D[Increment high_priority_counter]
D --> E{high_priority_counter >= max_high_before_low?}
E -- Yes --> F{Low-priority frame available?}
F -- Yes --> G[Process low-priority frame]
G --> H[Reset high_priority_counter]
F -- No --> I[Continue]
E -- No --> I
B -- No --> J{Low-priority frame available?}
J -- Yes --> K[Process low-priority frame]
J -- No --> I
I --> A
H --> A
K --> A
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

### 3.3 Connection Actor Overview

```mermaid
Expand Down
3 changes: 3 additions & 0 deletions docs/asynchronous-outbound-messaging-roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ design documents.
- [x] **Connection actor** with a biased `select!` loop that polls for shutdown,
high/low queues and response streams as described in
[Design §3.2][design-write-loop].
- [ ] **Fairness counter** to yield to the low-priority queue after bursts of
high-priority frames ([Design §3.2.1][design-fairness]).
- [ ] **Run state consolidation** using `Option` receivers and a closed source
counter ([Design §3.4][design-actor-state]).
- [X] **Internal protocol hooks** `before_send` and `on_command_end` invoked
Expand Down Expand Up @@ -57,6 +59,7 @@ design documents.
[design-actor-state]: asynchronous-outbound-messaging-design.md#34-actor-state-management
[design-dlq]: asynchronous-outbound-messaging-design.md#52-optional-dead-letter-queue-dlq-for-critical-messages
[design-errors]: asynchronous-outbound-messaging-design.md#5-error-handling--resilience
[design-fairness]: asynchronous-outbound-messaging-design.md#321-fairness-for-low-priority-frames
[design-hooks]: asynchronous-outbound-messaging-design.md#43-configuration-via-the-wireframeprotocol-trait
[design-push-handle]: asynchronous-outbound-messaging-design.md#41-the-pushhandle
[design-queues]: asynchronous-outbound-messaging-design.md#31-prioritised-message-queues
Expand Down
4 changes: 3 additions & 1 deletion docs/multi-layered-testing-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,9 @@ inputs.
pushes, low-priority pushes, and multi-frame `Response::Stream`s for a single
connection. The test asserts that the final output stream respects the strict
priority order (`shutdown > high > low > stream`) and that no frames are ever
lost or reordered within their own channel.
lost or reordered within their own channel. When the fairness counter is
configured, sequences containing continuous high-priority pushes must still
observe periodic low-priority frames.

**Measurable Objective:** The test suite must pass **1,000,000 generated test
cases**, verifying frame ordering and completeness on every run.
Expand Down