diff --git a/docs/asynchronous-outbound-messaging-design.md b/docs/asynchronous-outbound-messaging-design.md index e8162e2c..b2fc47b6 100644 --- a/docs/asynchronous-outbound-messaging-design.md +++ b/docs/asynchronous-outbound-messaging-design.md @@ -39,13 +39,13 @@ sections describe how to build that actor from first principles using the biased The implementation must satisfy the following core requirements: -| ID | Requirement | +| ID | Requirement | | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -| G1 | Any async task must be able to push frames to a live connection. | -| G2 | Ordering-safety: Pushed frames must interleave correctly with normal request/response traffic and respect any per-message sequencing rules. | -| G3 | Back-pressure: Writers must block (or fail fast) when the peer cannot drain the socket, preventing unbounded memory consumption. | -| G4 | Generic—independent of any particular protocol; usable by both servers and clients built on wireframe. | -| G5 | Preserve the simple “return a reply” path for code that does not need pushes, ensuring backward compatibility and low friction for existing users. | +| G1 | Any async task must be able to push frames to a live connection. | +| G2 | Ordering-safety: Pushed frames must interleave correctly with normal request/response traffic and respect any per-message sequencing rules. | +| G3 | Back-pressure: Writers must block (or fail fast) when the peer cannot drain the socket, preventing unbounded memory consumption. | +| G4 | Generic—independent of any particular protocol; usable by both servers and clients built on wireframe. | +| G5 | Preserve the simple “return a reply” path for code that does not need pushes, ensuring backward compatibility and low friction for existing users. | ## 3. Core Architecture: The Connection Actor @@ -70,7 +70,7 @@ manage two distinct, bounded `tokio::mpsc` channels for pushed frames: messages like heartbeats, session control notifications, or protocol-level pings. -1. `low_priority_push_rx: mpsc::Receiver`: For standard, non-urgent +2. `low_priority_push_rx: mpsc::Receiver`: For standard, non-urgent background messages like log forwarding or secondary status updates. The bounded nature of these channels provides an inherent and robust @@ -90,13 +90,13 @@ The polling order will be: 1. **Graceful Shutdown Signal:** The `CancellationToken` will be checked first to ensure immediate reaction to a server-wide shutdown request. -1. **High-Priority Push Channel:** Messages from `high_priority_push_rx` will be +2. **High-Priority Push Channel:** Messages from `high_priority_push_rx` will be drained next. -1. **Low-Priority Push Channel:** Messages from `low_priority_push_rx` will be +3. **Low-Priority Push Channel:** Messages from `low_priority_push_rx` will be processed after all high-priority messages. -1. **Handler Response Stream:** Frames from the active request's +4. **Handler Response Stream:** Frames from the active request's `Response::Stream` will be processed last. ```rust @@ -684,11 +684,11 @@ sequenceDiagram ## 8. Measurable Objectives & Success Criteria -| Category | Objective | Success Metric | +| Category | Objective | Success Metric | | --------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| API Correctness | The PushHandle, SessionRegistry, and WireframeProtocol trait are implemented exactly as specified in this document. | 100% of the public API surface is present and correctly typed. | -| Functionality | Pushed frames are delivered reliably and in the correct order of priority. | A test with concurrent high-priority, low-priority, and streaming producers must show that all frames are delivered and that the final written sequence respects the strict priority order. | -| Back-pressure | A slow consumer must cause producer tasks to suspend without consuming unbounded memory. | A test with a slow consumer and a fast producer must show the producer's push().await call blocks, and the process memory usage remains stable. | -| Resilience | The SessionRegistry must not leak memory when connections are terminated. | A long-running test that creates and destroys thousands of connections must show no corresponding growth in the SessionRegistry's size or the process's overall memory footprint. | -| Performance | The overhead of the push mechanism should be minimal for connections that do not use it. | A benchmark of a simple request-response workload with the push feature enabled (but unused) should show < 2% performance degradation compared to a build without the feature. | -| Performance | The latency for a high-priority push under no contention should be negligible. | The time from push_high_priority().await returning to the frame being written to the socket buffer should be < 10µs. | +| API Correctness | The PushHandle, SessionRegistry, and WireframeProtocol trait are implemented exactly as specified in this document. | 100% of the public API surface is present and correctly typed. | +| Functionality | Pushed frames are delivered reliably and in the correct order of priority. | A test with concurrent high-priority, low-priority, and streaming producers must show that all frames are delivered and that the final written sequence respects the strict priority order. | +| Back-pressure | A slow consumer must cause producer tasks to suspend without consuming unbounded memory. | A test with a slow consumer and a fast producer must show the producer's push().await call blocks, and the process memory usage remains stable. | +| Resilience | The SessionRegistry must not leak memory when connections are terminated. | A long-running test that creates and destroys thousands of connections must show no corresponding growth in the SessionRegistry's size or the process's overall memory footprint. | +| Performance | The overhead of the push mechanism should be minimal for connections that do not use it. | A benchmark of a simple request-response workload with the push feature enabled (but unused) should show < 2% performance degradation compared to a build without the feature. | +| Performance | The latency for a high-priority push under no contention should be negligible. | The time from push_high_priority().await returning to the frame being written to the socket buffer should be < 10µs. | diff --git a/docs/asynchronous-outbound-messaging-roadmap.md b/docs/asynchronous-outbound-messaging-roadmap.md index 8e60f785..9e8927a9 100644 --- a/docs/asynchronous-outbound-messaging-roadmap.md +++ b/docs/asynchronous-outbound-messaging-roadmap.md @@ -29,8 +29,10 @@ design documents. - [x] **Leak-proof `SessionRegistry`** using `dashmap::DashMap` and `Weak` pointers ([Design §4.2][design-registry], [Resilience Guide §3.2][resilience-registry]). -- [ ] **Document `async-stream`** for creating `Response::Stream` values +- [x] **Document `async-stream`** for creating `Response::Stream` values ([Roadmap #2.4][roadmap-2-4]). +- [ ] **Example handler using `async-stream`** demonstrating `Response::Stream` + generation in the examples directory. - [ ] **Tests covering streams and push delivery** drawing on [Testing Guide §4][testing-guide-advanced]. diff --git a/docs/multi-packet-and-streaming-responses-design.md b/docs/multi-packet-and-streaming-responses-design.md index a97dc71f..fd97407f 100644 --- a/docs/multi-packet-and-streaming-responses-design.md +++ b/docs/multi-packet-and-streaming-responses-design.md @@ -38,11 +38,10 @@ The implementation must satisfy the following core requirements: ## 3. Core Architecture: Declarative Streaming The cornerstone of this design is a move to a purely **declarative streaming -model**. Instead of providing handlers with an imperative `FrameSink` to push -frames into, handlers will declaratively return a description of the entire -response. This approach significantly simplifies the API surface, improves -testability, and eliminates a class of resource management issues associated -with sink-based designs. +model**. Handlers declaratively return a description of the entire response. +This approach significantly simplifies the API surface, improves testability, +and eliminates a class of resource management issues associated with sink-based +designs. ### 3.1 The Connection Actor's Role @@ -61,12 +60,12 @@ explicit channel management. ### 3.2 The `async-stream` Crate To provide an ergonomic way for developers to generate streams using -imperative-style logic (e.g., inside a `for` loop), `wireframe` will adopt and -recommend the `async-stream` crate. This crate provides macros (`stream!` and +imperative-style logic (e.g., inside a `for` loop), `wireframe` adopts and +recommends the `async-stream` crate. This crate provides macros (`stream!` and `try_stream!`) that transform imperative `yield` statements into a fully -compliant `Stream` object. This gives developers the best of both worlds: the -intuitive feel of imperative code generation without the API complexity of a -separate `FrameSink` type. +compliant `Stream` object. This gives developers the intuitive feel of +imperative code generation with minimal API complexity. The library recommends +this pattern as the canonical way to build `Response::Stream` values. ## 4. Public API Surface @@ -252,7 +251,7 @@ hang. | Category | Objective | Success Metric | | API Correctness | The Response enum and FrameStream type alias are implemented exactly as specified in this document. | 100% of the public API surface is present and correctly typed. | | Functionality | A handler returning a stream of N frames results in N frames being written to the socket in the correct order. | A test suite confirms 100% frame delivery and strict ordering for Response::Vec and Response::Stream. | -| Ergonomics | The async-stream pattern is documented as the canonical approach for dynamic stream generation and is demonstrably simpler than a FrameSink API. | The official examples and documentation exclusively use the declarative Response model. | +| Ergonomics | The async-stream pattern is documented as the canonical approach for dynamic stream generation. | The official examples and documentation exclusively use the declarative Response model. | | Performance | The Response::Vec variant has measurably lower allocation and dispatch overhead than Response::Stream for small, fixed-size responses. | A criterion benchmark confirms that Response::Vec is at least 50% faster and performs fewer allocations than Response::Stream for a response of 10 frames. | | Error Handling | A WireframeError::Protocol error yielded from a stream correctly triggers the handle_error protocol callback without terminating the connection. | An integration test confirms that a protocol-level error is correctly formatted and sent to the client, while the connection remains open. | diff --git a/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md b/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md index 1748dce2..7bc3b2e5 100644 --- a/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md +++ b/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md @@ -40,10 +40,9 @@ server-initiated pushes and streaming responses. #### The Unified `Response` Enum and Declarative Handler Model -To provide a clean, unified API, the handler return type will evolve. The -imperative `FrameSink` model, which required a separate handler signature and -introduced resource management complexities, will be replaced by a more -ergonomic, declarative approach. 1 Handlers will return an enhanced +To provide a clean, unified API, the handler return type will evolve. A more +ergonomic, declarative approach replaces the previous imperative model. +Handlers will return an enhanced `Response` enum, giving developers clear and efficient ways to express their intent. @@ -64,11 +63,11 @@ pub enum Response { ``` This design is powered by the `async-stream` crate, which allows developers to -write imperative-looking logic that generates a declarative `Stream` object. -This provides the best of both worlds: the intuitive feel of a - -`for` loop for generating frames, without the API complexity of a separate -`Sink` type. +write imperative-looking logic that generates a declarative `Stream` object. It +provides the best of both worlds: the intuitive feel of a `for` loop for +generating frames with minimal API complexity. The project recommends +`async-stream` as the canonical method for constructing `Response::Stream` +values. Rust diff --git a/docs/wireframe-1-0-detailed-development-roadmap.md b/docs/wireframe-1-0-detailed-development-roadmap.md index 482d40ea..71a053f5 100644 --- a/docs/wireframe-1-0-detailed-development-roadmap.md +++ b/docs/wireframe-1-0-detailed-development-roadmap.md @@ -20,14 +20,14 @@ public consumption. message processing. This phase establishes the internal architecture upon which all public-facing features will be built.* -| Item | Name | Details | -| ---- | ---- | ------- | -| 1.1 | Core Response & Error Types | Define the new `Response` enum with `Single`, `Vec`, `Stream` and `Empty` variants. Implement the generic `WireframeError` enum to distinguish between I/O and protocol errors. | Small | - | -| 1.2 | Priority Push Channels | Implement the internal dual-channel `mpsc` mechanism within the connection state to handle high-priority and low-priority pushed frames. | Medium | - | -| 1.3 | Connection Actor Write Loop | Convert the per-request workers into stateful connection actors. Implement a `select!(biased; ...)` loop that polls for shutdown signals, high/low priority pushes and the handler response stream in that strict order. | Large | #1.2 | -| 1.4 | Initial FragmentStrategy Trait | Define the initial `FragmentStrategy` trait and the `FragmentMeta` struct. Focus on the core methods: `decode_header` and `encode_header`. | Medium | - | -| 1.5 | Basic FragmentAdapter | Implement the `FragmentAdapter` as a `FrameProcessor`. Build the inbound reassembly logic for a single, non-multiplexed stream of fragments and the outbound logic for splitting a single large frame. | Large | #1.4 | -| 1.6 | Internal Hook Plumbing | Add the invocation points for the protocol-specific hooks (`before_send`, `on_command_end`, etc.) within the connection actor, even if the public trait is not yet defined. | Small | #1.3 | +| Item | Name | Details | Size | Depends | +| ---- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------- | +| 1.1 | Core Response & Error Types | Define the new `Response` enum with `Single`, `Vec`, `Stream` and `Empty` variants. Implement the generic `WireframeError` enum to distinguish between I/O and protocol errors. | Small | - | +| 1.2 | Priority Push Channels | Implement the internal dual-channel `mpsc` mechanism within the connection state to handle high-priority and low-priority pushed frames. | Medium | - | +| 1.3 | Connection Actor Write Loop | Convert per-request workers into stateful connection actors. Implement a `select!(biased; ...)` loop that polls for shutdown signals, high/low priority pushes and the handler response stream in that strict order. | Large | #1.2 | +| 1.4 | Initial FragmentStrategy Trait | Define the initial `FragmentStrategy` trait and the `FragmentMeta` struct. Focus on the core methods: `decode_header` and `encode_header`. | Medium | - | +| 1.5 | Basic FragmentAdapter | Implement the `FragmentAdapter` as a `FrameProcessor`. Build the inbound reassembly logic for a single, non-multiplexed stream of fragments and the outbound logic for splitting a single large frame. | Large | #1.4 | +| 1.6 | Internal Hook Plumbing | Add the invocation points for the protocol-specific hooks (`before_send`, `on_command_end`, etc.) within the connection actor, even if the public trait is not yet defined. | Small | #1.3 | ## Phase 2: Public APIs & Developer Ergonomics @@ -35,14 +35,14 @@ all public-facing features will be built.* and idiomatic API. This phase is about making the powerful new mechanics usable and intuitive.* -| Item | Name | Details | -| ---- | ---- | ------- | -| 2.1 | WireframeProtocol Trait & Builder | Define the cohesive `WireframeProtocol` trait to encapsulate all protocol-specific logic. Refactor the `WireframeApp` builder to use a fluent `.with_protocol(MyProtocol)` method instead of multiple closures. | Medium | #1.6 | -| 2.2 | Public PushHandle API | Implement the public `PushHandle` struct with its `push`, `try_push` and policy-based `push_with_policy` methods. This handle will interact with the dual-channel system from #1.2. | Medium | #1.2 | -| 2.3 | Leak-Proof SessionRegistry | Implement the `SessionRegistry` for discovering connection handles. This must use `dashmap` with `Weak` pointers to prevent memory leaks from terminated connections. | Medium | #2.2 | -| 2.4 | async-stream Integration & Docs | Remove the proposed `FrameSink` from the design. Update the `Response::Stream` handling and write documentation recommending `async-stream` as the canonical way to create streams imperatively. | Small | #1.1 | -| 2.5 | Initial Test Suite | Write unit and integration tests for the new public APIs. Verify that `Response::Vec` and `Response::Stream` work, and that `PushHandle` can successfully send frames that are received by a client. | Large | #2.1, #2.3, #2.4 | -| 2.6 | Basic Fragmentation Example | Implement a simple `FragmentStrategy` (e.g. `LenFlag32K`) and an example showing the `FragmentAdapter` in use. This validates the adapter's basic functionality. | Medium | #1.5, #2.5 | +| Item | Name | Details | Size | Depends on | +| ---- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------------- | +| 2.1 | WireframeProtocol Trait & Builder | Define the cohesive `WireframeProtocol` trait to encapsulate all protocol-specific logic. Refactor the `WireframeApp` builder to use a fluent `.with_protocol(MyProtocol)` method instead of multiple closures. | Medium | #1.6 | +| 2.2 | Public PushHandle API | Implement the public `PushHandle` struct with its `push`, `try_push` and policy-based `push_with_policy` methods. This handle interacts with the dual-channel system from #1.2. | Medium | #1.2 | +| 2.3 | Leak-Proof SessionRegistry | Implement the `SessionRegistry` for discovering connection handles. This must use `dashmap` with `Weak` pointers to prevent memory leaks from terminated connections. | Medium | #2.2 | +| 2.4 | async-stream Integration & Docs | Remove the proposed `FrameSink` from the design. Update the `Response::Stream` handling and document `async-stream` as the canonical way to create streams imperatively. | Small | #1.1 | +| 2.5 | Initial Test Suite | Write unit and integration tests for the new public APIs. Verify that `Response::Vec` and `Response::Stream` work, and that `PushHandle` can successfully send frames that are received by a client. | Large | #2.1, #2.3, #2.4 | +| 2.6 | Basic Fragmentation Example | Implement a simple `FragmentStrategy` (e.g. `LenFlag32K`) and an example showing the `FragmentAdapter` in use. This validates the adapter's basic functionality. | Medium | #1.5, #2.5 | ## Phase 3: Production Hardening & Resilience @@ -50,24 +50,24 @@ and intuitive.* operation in a production environment. This phase moves the library from "functional" to "resilient".* -| Item | Name | Details | -| ---- | ---- | ------- | -| 3.1 | Graceful Shutdown | Implement the server-wide graceful shutdown pattern. Use tokio_util::sync::CancellationToken for signalling and tokio_util::task::TaskTracker to ensure all connection actors terminate cleanly. | Large | #1.3 | -| 3.2 | Re-assembly DoS Protection | Harden the FragmentAdapter by adding a non-optional, configurable timeout for partial message re-assembly and strictly enforcing the max_message_size limit to prevent memory exhaustion. | Medium | #1.5 | -| 3.3 | Multiplexed Re-assembly | Enhance the FragmentAdapter's inbound logic to support concurrent re-assembly of multiple messages. Use the msg_id from FragmentMeta as a key into a dashmap::DashMap of partial messages. | Large | #3.2 | -| 3.4 | Per-Connection Rate Limiting | Integrate an asynchronous, token-bucket rate limiter into the PushHandle. The rate limit should be configurable on the WireframeApp builder and enforced on every push. | Medium | #2.2 | -| 3.5 | Dead Letter Queue (DLQ) | Implement the optional Dead Letter Queue mechanism. Allow a user to provide a DLQ channel sender during app setup; failed pushes (due to a full queue) can be routed there instead of being dropped. | Medium | #2.2 | -| 3.6 | Context-Aware FragmentStrategy | Enhance the FragmentStrategy trait. max_fragment_payload and encode_header should receive a reference to the logical Frame being processed, allowing for more dynamic fragmentation rules. | Small | #1.4 | +| Item | Name | Details | Size | Depends on | +| ---- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------- | +| 3.1 | Graceful Shutdown | Implement the server-wide graceful shutdown pattern. Use `tokio_util::sync::CancellationToken` for signalling and `tokio_util::task::TaskTracker` to ensure all connection actors terminate cleanly. | Large | #1.3 | +| 3.2 | Re-assembly DoS Protection | Harden the `FragmentAdapter` by adding a non-optional, configurable timeout for partial message re-assembly and strictly enforcing the `max_message_size` limit to prevent memory exhaustion. | Medium | #1.5 | +| 3.3 | Multiplexed Re-assembly | Enhance the `FragmentAdapter`'s inbound logic to support concurrent re-assembly of multiple messages. Use the `msg_id` from `FragmentMeta` as a key into a `dashmap::DashMap` of partial messages. | Large | #3.2 | +| 3.4 | Per-Connection Rate Limiting | Integrate an asynchronous, token-bucket rate limiter into the `PushHandle`. The rate limit should be configurable on the `WireframeApp` builder and enforced on every push. | Medium | #2.2 | +| 3.5 | Dead Letter Queue (DLQ) | Implement the optional Dead Letter Queue mechanism. Allow a user to provide a DLQ channel sender during app setup; failed pushes (due to a full queue) can be routed there instead of being dropped. | Medium | #2.2 | +| 3.6 | Context-Aware FragmentStrategy | Enhance the `FragmentStrategy` trait. `max_fragment_payload` and `encode_header` should receive a reference to the logical `Frame` being processed, allowing for more dynamic fragmentation rules. | Small | #1.4 | *Focus: Finalizing the library with comprehensive instrumentation, advanced testing, and high-quality documentation to ensure it is stable, debuggable, and ready for a 1.0 release.* -| Item | Name | Details | -| ---- | ---- | ------- | -| 4.1 | Pervasive tracing instrumentation | Instrument the entire library with `tracing`. Add `span!` calls for connection and request lifecycles and detailed `event!` calls for key state transitions (e.g., back-pressure applied, frame dropped, connection terminated). | Large | All | -| 4.2 | Advanced Testing: Concurrency & Logic | Implement the advanced test suite. Use loom to verify the concurrency correctness of the select! loop and PushHandle. Use proptest for stateful property-based testing of complex protocol interactions (e.g., fragmentation and streaming). | Large | #3.3, #3.5 | -| 4.3 | Advanced Testing: Performance | Implement the criterion benchmark suite. Create micro-benchmarks for individual components (e.g., PushHandle contention) and macro-benchmarks for end-to-end throughput and latency. | Medium | All | -| 4.4 | Comprehensive User Guides | Write the official documentation for the new features. Create separate guides for "Duplex Messaging & Pushes", "Streaming Responses", and "Message Fragmentation". Each guide must include runnable examples and explain the relevant concepts and APIs. | Large | All | -| 4.5 | High-Quality Examples | Create at least two complete, high-quality examples demonstrating real-world use cases. These should include server-initiated MySQL packets (e.g., LOCAL INFILE and session-state trackers) and a push-driven protocol such as WebSocket heart-beats or MQTT broker fan-out. | Medium | All | -| 4.6 | Changelog & 1.0 Release | Finalize the CHANGELOG.md with a comprehensive summary of all new features, enhancements, and breaking changes. Tag and publish the 1.0.0 release. | Small | All | +| Item | Name | Details | Size | Depends on | +| ---- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------- | +| 4.1 | Pervasive tracing instrumentation | Instrument the entire library with `tracing`. Add `span!` calls for connection and request lifecycles and detailed `event!` calls for key state transitions (e.g., back-pressure applied, frame dropped, connection terminated). | Large | All | +| 4.2 | Advanced Testing: Concurrency & Logic | Implement the advanced test suite. Use `loom` to verify the concurrency correctness of the `select!` loop and `PushHandle`. Use `proptest` for stateful property-based testing of complex protocol interactions (e.g., fragmentation and streaming). | Large | #3.3, #3.5 | +| 4.3 | Advanced Testing: Performance | Implement the criterion benchmark suite. Create micro-benchmarks for individual components (e.g., `PushHandle` contention) and macro-benchmarks for end-to-end throughput and latency. | Medium | All | +| 4.4 | Comprehensive User Guides | Write the official documentation for the new features. Create separate guides for "Duplex Messaging & Pushes", "Streaming Responses", and "Message Fragmentation". Each guide must include runnable examples and explain the relevant concepts and APIs. | Large | All | +| 4.5 | High-Quality Examples | Create at least two complete, high-quality examples demonstrating real-world use cases. These should include server-initiated MySQL packets (e.g., LOCAL INFILE and session-state trackers) and a push-driven protocol such as WebSocket heart-beats or MQTT broker fan-out. | Medium | All | +| 4.6 | Changelog & 1.0 Release | Finalise the `CHANGELOG.md` with a comprehensive summary of all new features, enhancements, and breaking changes. Tag and publish the 1.0.0 release. | Small | All |