Skip to content

Define derived defaults for connection memory budgets#486

Merged
leynos merged 5 commits intomainfrom
define-derived-defaults-buffer-capacity-m2am4x
Mar 1, 2026
Merged

Define derived defaults for connection memory budgets#486
leynos merged 5 commits intomainfrom
define-derived-defaults-buffer-capacity-m2am4x

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Feb 27, 2026

Summary

Implements 8.3.5: derive memory budgets for connection budgets from buffer_capacity when explicit budgets are not provided. Budgets are computed lazily at connection time and wired through the inbound read path, with explicit budgets taking precedence. Adds unit tests, behavioural tests, and documentation updates to reflect the new derived-defaults behavior.

  • Derived defaults:
    • bytes_per_message = frame_budget × 16
    • bytes_per_connection = frame_budget × 64
    • bytes_in_flight = frame_budget × 64
  • Resolution:
    • Derived budgets are produced by default_memory_budgets(frame_budget) and resolved at runtime via resolve_effective_budgets(explicit, frame_budget).
    • If explicit budgets are provided, they override derived defaults.
  • Runtime integration:
    • Inbound processing now resolves effective budgets and propagates Some(effective_budgets) to message assembly and memory-pressure evaluation.
    • Exposed resolve_effective_budgets() through the frame_handling module for reuse.

Changes

  • Core functionality
    • src/app/builder_defaults.rs
      • Added default_memory_budgets(frame_budget) deriving budgets from frame budget.
      • Introduced constants for multipliers: 16, 64, 64.
    • src/app/frame_handling/backpressure.rs
      • Added resolve_effective_budgets(explicit, frame_budget) to derive or return explicit budgets.
    • src/app/frame_handling/mod.rs
      • Re-exported resolve_effective_budgets for wider access.
    • src/app/inbound_handler.rs
      • Wired budget derivation into process_stream: compute effective_budgets and pass them to assembly/evaluation paths as Some(effective_budgets).
  • Tests
    • Unit tests for derived defaults
      • src/app/builder_defaults.rs: tests verifying default_memory_budgets multipliers, scaling, clamping, and alignment with fragmentation multiplier.
    • Unit tests for budget resolution
      • src/app/frame_handling/backpressure_tests.rs: added tests for resolve_effective_budgets with explicit vs derived budgets and frame-budget scaling.
    • Behavioural tests (BDD)
      • New: tests/features/derived_memory_budgets.feature
      • New fixtures: tests/fixtures/derived_memory_budgets.rs
      • New steps: tests/steps/derived_memory_budgets_steps.rs
      • New scenarios: tests/scenarios/derived_memory_budgets_scenarios.rs
      • Wire-in in test harness: tests/fixtures/mod.rs, tests/steps/mod.rs, tests/scenarios/mod.rs
  • Documentation
    • docs/adr-002-streaming-requests-and-shared-message-assembly.md
      • Added implementation decisions for 8.3.5: derived defaults, lazy derivation, and precedence.
    • docs/users-guide.md
      • Documented Derived budget defaults: how budgets are derived, default multipliers, and override behavior.
    • docs/roadmap.md
      • Marked 8.3.5 as done (x).
    • docs/execplans/8-3-5-Define derived defaults based on buffer_capacity.md
      • New execplan detailing scope, decisions, and tests.

Testing plan

  • Unit tests:
    • cargo test --lib builder_defaults
    • cargo test --lib frame_handling
  • Behavioural tests:
    • cargo test --test bdd --all-features derived_memory_budgets
  • Lint/format checks as part of CI (make lint, make fmt, etc.)

Acceptance criteria

  • When memory_budgets(...) is not called, budgets are derived from buffer_capacity with the three-tier protection active.
  • Explicit budgets override derived defaults (ADR-002 precedence rule).
  • Derived budgets scale proportionally with buffer_capacity (e.g., from 1024-byte default).
  • All existing budget enforcement tests continue to pass; new unit and BDD tests pass for derived/default behavior.
  • Documentation and roadmap reflect 8.3.5 as completed.

Notes

  • No public API surface changes beyond internal helpers; consumer-visible behavior remains consistent except for derived defaults now being active by default when budgets are not explicitly set.
  • Dynamiques: derivation is lazy and automatically reacts to buffer_capacity/codecs changes without builder changes.

📎 Task: https://www.devboxer.com/task/e2e75d74-42b5-4427-8981-65224a8a654a

Implement roadmap item 8.3.5 to derive sensible default per-message, per-connection, and in-flight memory budgets based on the codec's buffer_capacity (max_frame_length) when explicit budgets are not set. Defaults use multipliers aligned with fragmentation defaults: per_message = frame_budget × 16, per_connection = frame_budget × 64, bytes_in_flight = frame_budget × 64.

Budgets are computed lazily at connection time in process_stream via resolve_effective_budgets(), preserving existing builder APIs and respecting explicit budget overrides.

Add unit tests for default_memory_budgets() and resolve_effective_budgets(), and behavioral tests validating derived budgets enforce protection tiers and explicit budgets override defaults.

Update ADR-002 with implementation decisions, users guide with derived budget defaults, and mark roadmap item 8.3.5 done.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Feb 27, 2026

Reviewer's Guide

Implements lazy, derived default per-connection memory budgets from codec buffer_capacity/frame_budget, wires them into inbound processing via a resolve_effective_budgets helper, and adds tests and docs (including an execplan and ADR updates) to validate and document the new behavior and roadmap item 8.3.5.

Sequence diagram for resolving and applying effective memory budgets

sequenceDiagram
    actor Client
    participant WireframeApp
    participant FrameHandling as FrameHandling
    participant Backpressure as Backpressure
    participant BuilderDefaults as BuilderDefaults
    participant MessageAssembly as MessageAssemblyState

    Client->>WireframeApp: process_stream(stream)
    WireframeApp->>WireframeApp: codec.max_frame_length() -> requested_frame_length
    WireframeApp->>WireframeApp: framed.read_buffer_mut().reserve(max_frame_length)

    WireframeApp->>FrameHandling: resolve_effective_budgets(self.memory_budgets, requested_frame_length)
    activate FrameHandling
    FrameHandling->>Backpressure: resolve_effective_budgets(explicit, frame_budget)
    activate Backpressure
    Backpressure->>BuilderDefaults: default_memory_budgets(frame_budget) [when explicit is None]
    activate BuilderDefaults
    BuilderDefaults-->>Backpressure: derived MemoryBudgets
    deactivate BuilderDefaults
    Backpressure-->>FrameHandling: MemoryBudgets (effective_budgets)
    deactivate Backpressure
    FrameHandling-->>WireframeApp: MemoryBudgets (effective_budgets)
    deactivate FrameHandling

    WireframeApp->>FrameHandling: new_message_assembly_state(self.fragmentation, requested_frame_length, Some(effective_budgets))
    FrameHandling-->>WireframeApp: Option MessageAssemblyState
    WireframeApp->>WireframeApp: FramePipeline::new(self.fragmentation)

    loop read_frames
        WireframeApp->>FrameHandling: evaluate_memory_pressure(message_assembly.as_ref(), Some(effective_budgets))
        FrameHandling-->>WireframeApp: Pressure
        WireframeApp->>FrameHandling: apply_memory_pressure(Pressure, purge_expired)
        FrameHandling-->>WireframeApp: (may sleep, purge)
        WireframeApp->>MessageAssembly: process frame under budgets
    end
Loading

Class diagram for derived memory budgets and inbound handling

classDiagram
    direction LR

    class WireframeApp {
        +process_stream(stream)
        -Option~MemoryBudgets~ memory_budgets
        -Option~FragmentationConfig~ fragmentation
    }

    class MemoryBudgets {
        +BudgetBytes bytes_per_message()
        +BudgetBytes bytes_per_connection()
        +BudgetBytes bytes_in_flight()
        +new(per_message, per_connection, in_flight) MemoryBudgets
    }

    class BudgetBytes {
        +new(value) BudgetBytes
        +as_usize() usize
    }

    class BuilderDefaults {
        <<module>>
        +default_fragmentation(frame_budget usize) Option~FragmentationConfig~
        +default_memory_budgets(frame_budget usize) MemoryBudgets
        -DEFAULT_MESSAGE_SIZE_MULTIPLIER usize
        -DEFAULT_MESSAGE_BUDGET_MULTIPLIER usize
        -DEFAULT_CONNECTION_BUDGET_MULTIPLIER usize
        -DEFAULT_IN_FLIGHT_BUDGET_MULTIPLIER usize
    }

    class Backpressure {
        <<module>>
        +resolve_effective_budgets(explicit Option~MemoryBudgets~, frame_budget usize) MemoryBudgets
        +evaluate_memory_pressure(message_assembly Option~MessageAssemblyState~, budgets Option~MemoryBudgets~) Pressure
        +apply_memory_pressure(pressure Pressure, purge_fn)
        -active_aggregate_limit_bytes(budgets MemoryBudgets) usize
    }

    class FrameHandlingMod {
        <<module>>
        +new_message_assembly_state(fragmentation Option~FragmentationConfig~, frame_budget usize, budgets Option~MemoryBudgets~) Option~MessageAssemblyState~
        +evaluate_memory_pressure(message_assembly Option~MessageAssemblyState~, budgets Option~MemoryBudgets~) Pressure
        +apply_memory_pressure(pressure Pressure, purge_fn)
        +resolve_effective_budgets(explicit Option~MemoryBudgets~, frame_budget usize) MemoryBudgets
    }

    class MessageAssemblyState {
        +with_budgets(fragmentation Option~FragmentationConfig~, frame_budget usize, budgets Option~MemoryBudgets~) MessageAssemblyState
        +new(fragmentation Option~FragmentationConfig~, frame_budget usize) MessageAssemblyState
    }

    class FramePipeline {
        +new(fragmentation Option~FragmentationConfig~) FramePipeline
    }

    WireframeApp --> FrameHandlingMod : uses
    WireframeApp --> MemoryBudgets : holds Option
    FrameHandlingMod --> Backpressure : reexports
    FrameHandlingMod --> BuilderDefaults : uses via Backpressure
    Backpressure --> MemoryBudgets : resolves
    BuilderDefaults --> MemoryBudgets : constructs
    MemoryBudgets --> BudgetBytes : composed_of
    MessageAssemblyState --> MemoryBudgets : uses
    FramePipeline --> MessageAssemblyState : coordinates
Loading

File-Level Changes

Change Details Files
Introduce default_memory_budgets and constants to derive per-message, per-connection, and in-flight memory budgets from the frame budget, with unit tests.
  • Add DEFAULT_MESSAGE_BUDGET_MULTIPLIER/DEFAULT_CONNECTION_BUDGET_MULTIPLIER/DEFAULT_IN_FLIGHT_BUDGET_MULTIPLIER constants aligned with fragmentation defaults.
  • Implement default_memory_budgets(frame_budget) that clamps the frame budget, applies multipliers, and builds MemoryBudgets using BudgetBytes and NonZeroUsize.
  • Add rstest-based unit tests covering multiplier correctness, scaling with frame_budget, clamping to min/max frame lengths, and alignment of per-message budget with DEFAULT_MESSAGE_SIZE_MULTIPLIER.
src/app/builder_defaults.rs
Add resolve_effective_budgets helper and re-export it so inbound handling can choose between explicit and derived budgets at runtime, with unit tests for resolution behavior.
  • Import default_memory_budgets into backpressure and implement resolve_effective_budgets(explicit, frame_budget) that returns explicit budgets if present or derived defaults otherwise.
  • Re-export resolve_effective_budgets from the frame_handling module alongside existing backpressure helpers.
  • Add rstest unit tests verifying explicit budgets are preferred, derived budgets are produced when None, and derived budgets vary with frame_budget.
src/app/frame_handling/backpressure.rs
src/app/frame_handling/backpressure_tests.rs
src/app/frame_handling/mod.rs
Wire effective memory budgets into inbound processing so every connection uses either explicit or derived budgets consistently for assembly and backpressure.
  • In process_stream, compute effective_budgets once per connection via frame_handling::resolve_effective_budgets(self.memory_budgets, requested_frame_length).
  • Pass Some(effective_budgets) into new_message_assembly_state instead of the raw self.memory_budgets option.
  • Pass Some(effective_budgets) into evaluate_memory_pressure so backpressure is always evaluated against a concrete budget set.
src/app/inbound_handler.rs
Add BDD fixtures, steps, scenarios, and feature definitions to validate derived budget behavior and explicit override semantics.
  • Introduce DerivedMemoryBudgetsWorld fixture that spins up a WireframeApp using either derived or explicit budgets, drives a duplex client/server pair, and captures delivered payloads and connection errors.
  • Define step functions with a derived-budget prefix to configure apps, send first/continuation frames over key ranges, and assert connection aborts or successful payload delivery.
  • Register new fixture, steps, and scenarios modules and add a derived_memory_budgets.feature file with scenarios for enforcement, within-limits delivery, and explicit-budget override.
tests/fixtures/derived_memory_budgets.rs
tests/steps/derived_memory_budgets_steps.rs
tests/scenarios/derived_memory_budgets_scenarios.rs
tests/features/derived_memory_budgets.feature
tests/fixtures/mod.rs
tests/steps/mod.rs
tests/scenarios/mod.rs
Update architecture/docs and roadmap to describe the derived budget defaults and mark roadmap item 8.3.5 complete, including a detailed execution plan.
  • Extend ADR-002 with an "Implementation decisions (2026-02-27)" section describing how derived budgets are computed from codec.max_frame_length, the chosen multipliers, lazy derivation in process_stream, and explicit-vs-derived precedence.
  • Add a "Derived budget defaults" subsection to the users guide that documents the multiplier table, default values for a 1024-byte frame, activation of all three protection tiers, and override behavior when memory_budgets is configured.
  • Mark roadmap item 8.3.5 as done and add a new execplan document describing scope, constraints, risks, tests, and outcomes for defining derived defaults based on buffer_capacity.
docs/adr-002-streaming-requests-and-shared-message-assembly.md
docs/users-guide.md
docs/roadmap.md
docs/execplans/8-3-5-Define derived defaults based on buffer_capacity.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 27, 2026

Walkthrough

Summarise the addition of derived default memory budgets computed from frame size when explicit memory_budgets(...) are not set; thread the resolved effective budgets into the inbound read path and backpressure evaluation; add helpers, tests, BDD fixtures, and documentation updates.

Changes

Cohort / File(s) Summary
Documentation & Architecture
docs/adr-002-streaming-requests-and-shared-message-assembly.md, docs/execplans/8-3-5-Define derived defaults based on buffer_capacity.md, docs/users-guide.md, docs/roadmap.md
Add ADR and execplan describing derived-default memory budgets, multipliers and enforcement semantics; update user guide and mark roadmap item complete.
Budget Derivation Logic
src/app/builder_defaults.rs
Add default_memory_budgets(frame_budget) with internal derive_budget() and three multipliers to compute per-message, per-connection and in-flight BudgetBytes (with clamping) and tests validating scaling/clamping.
Backpressure & Budget Resolution
src/app/frame_handling/backpressure.rs, src/app/frame_handling/mod.rs
Add resolve_effective_budgets(explicit, frame_budget) returning explicit budgets or derived defaults; expose via pub(crate) re-export; adjust soft-limit helper.
Inbound Handler Integration
src/app/inbound_handler.rs
Compute effective_budgets = resolve_effective_budgets(self.memory_budgets, requested_frame_length) and pass Some(effective_budgets) into new_message_assembly_state and evaluate_memory_pressure.
Unit Tests
src/app/frame_handling/backpressure_tests.rs
Add tests for resolve_effective_budgets covering explicit return, derivation when absent, and scaling with frame budget.
BDD Feature & Scenarios
tests/features/derived_memory_budgets.feature, tests/scenarios/derived_memory_budgets_scenarios.rs, tests/scenarios/mod.rs
Add BDD feature describing three scenarios and scenario glue modules to exercise derived/explicit budget behaviours.
Test Fixtures & Steps
tests/fixtures/derived_memory_budgets.rs, tests/fixtures/mod.rs, tests/steps/derived_memory_budgets_steps.rs, tests/steps/mod.rs
Add DerivedMemoryBudgetsWorld fixture, ExplicitBudgetConfig parser, helper methods for starting apps (derived and explicit), frame sending helpers, assertions, and BDD step definitions.

Sequence Diagram

sequenceDiagram
    participant App as WireframeApp (Builder)
    participant Handler as InboundHandler (process_stream)
    participant Resolver as resolve_effective_budgets()
    participant Memory as new_message_assembly_state()
    participant Pressure as evaluate_memory_pressure()

    App->>Handler: Frame received (requested_frame_length)
    Handler->>Resolver: resolve_effective_budgets(explicit_budgets?, frame_length)
    alt Explicit budgets set
        Resolver-->>Handler: return explicit budgets
    else No explicit budgets
        Resolver->>Resolver: derive from frame_length via multipliers (16×, 64×, 64×)
        Resolver-->>Handler: return derived budgets
    end
    Handler->>Memory: new_message_assembly_state(Some(effective_budgets))
    Handler->>Pressure: evaluate_memory_pressure(Some(effective_budgets))
    Pressure-->>Handler: MemoryPressureAction (BackOff | Abort)
    Handler-->>App: ProcessingResult
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

💾 When budgets lie silent, compute and rise,
Frame-size births defaults with tidy ties,
Sixteen then sixty-four—protect and flow,
Soft whispers pause, hard limits let go,
Tests march, docs sing, the budgets now wise.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title accurately summarises the main change: implementing derived defaults for connection memory budgets from buffer_capacity when explicit budgets are not provided.
Description check ✅ Passed The description comprehensively details the implementation of roadmap item 8.3.5, covering derived budget formulas, resolution logic, code changes, tests, and documentation updates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch define-derived-defaults-buffer-capacity-m2am4x

Comment @coderabbitai help to get the list of available commands and usage tips.

@leynos leynos marked this pull request as ready for review February 27, 2026 01:09
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • In builder_defaults.rs, DEFAULT_MESSAGE_BUDGET_MULTIPLIER is intended to stay aligned with DEFAULT_MESSAGE_SIZE_MULTIPLIER; consider deriving it directly from that constant or reusing the same value to avoid silent divergence if one is changed in future.
  • The derived-memory-budgets fixture reimplements fragmentation derivation (buffer_capacity * 16 and a 30s timeout); using default_fragmentation or otherwise sharing the same helper as production code would reduce duplication and keep the test behaviour automatically aligned with the real defaults.
  • In DerivedMemoryBudgetsWorld::start_app_derived/start_app_explicit, the fragmentation config uses the raw buffer_capacity while WireframeApp::buffer_capacity applies clamp_frame_length, so basing the fragmentation frame budget on the clamped value (e.g. app.length_codec().max_frame_length()) would prevent potential inconsistencies for extreme capacities.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `builder_defaults.rs`, `DEFAULT_MESSAGE_BUDGET_MULTIPLIER` is intended to stay aligned with `DEFAULT_MESSAGE_SIZE_MULTIPLIER`; consider deriving it directly from that constant or reusing the same value to avoid silent divergence if one is changed in future.
- The derived-memory-budgets fixture reimplements fragmentation derivation (`buffer_capacity * 16` and a 30s timeout); using `default_fragmentation` or otherwise sharing the same helper as production code would reduce duplication and keep the test behaviour automatically aligned with the real defaults.
- In `DerivedMemoryBudgetsWorld::start_app_derived`/`start_app_explicit`, the fragmentation config uses the raw `buffer_capacity` while `WireframeApp::buffer_capacity` applies `clamp_frame_length`, so basing the fragmentation frame budget on the clamped value (e.g. `app.length_codec().max_frame_length()`) would prevent potential inconsistencies for extreme capacities.

## Individual Comments

### Comment 1
<location path="src/app/builder_defaults.rs" line_range="42" />
<code_context>
+const DEFAULT_CONNECTION_BUDGET_MULTIPLIER: usize = 64;
+const DEFAULT_IN_FLIGHT_BUDGET_MULTIPLIER: usize = 64;
+
+pub(super) fn default_memory_budgets(frame_budget: usize) -> MemoryBudgets {
+    let frame_budget = clamp_frame_length(frame_budget);
+    let per_message = NonZeroUsize::new(
</code_context>
<issue_to_address>
**issue (complexity):** Consider extracting the repeated budget derivation logic into a helper and simplifying the test structure to reduce duplication and clarify intent.

You can reduce duplication and make the intent clearer both in `default_memory_budgets` and the tests without changing behavior.

### 1. Factor out the repeated NonZero budget pattern

The three `NonZeroUsize::new(...).unwrap_or(NonZeroUsize::MIN)` blocks are identical except for the multiplier. A tiny helper makes the policy (“derive a non-zero budget from frame budget and multiplier, saturating, defaulting to MIN”) explicit and centralized:

```rust
fn derive_budget(frame_budget: usize, multiplier: usize) -> BudgetBytes {
    let bytes = NonZeroUsize::new(frame_budget.saturating_mul(multiplier))
        .unwrap_or(NonZeroUsize::MIN);
    BudgetBytes::new(bytes)
}

#[must_use]
pub(super) fn default_memory_budgets(frame_budget: usize) -> MemoryBudgets {
    let frame_budget = clamp_frame_length(frame_budget);
    MemoryBudgets::new(
        derive_budget(frame_budget, DEFAULT_MESSAGE_BUDGET_MULTIPLIER),
        derive_budget(frame_budget, DEFAULT_CONNECTION_BUDGET_MULTIPLIER),
        derive_budget(frame_budget, DEFAULT_IN_FLIGHT_BUDGET_MULTIPLIER),
    )
}
```

If there is a specific reason for the `unwrap_or(NonZeroUsize::MIN)` (e.g. overflow or a future change to `clamp_frame_length`), you can document it in `derive_budget` once instead of repeating it three times.

### 2. Simplify tests with `assert_eq!` and parameterization

The tests are currently very verbose, manually building `io::Error`s for simple equality checks. You can keep them as `rstest` tests but simplify to plain assertions and consolidate the repetitive structure:

```rust
#[cfg(test)]
mod tests {
    use super::*;
    use rstest::rstest;

    #[rstest]
    #[case(1024, 16_384, 65_536, 65_536)]
    #[case(4096, 4096 * 16, 4096 * 64, 4096 * 64)]
    fn default_budgets_scale_and_use_expected_multipliers(
        #[case] frame_budget: usize,
        #[case] expected_message: usize,
        #[case] expected_connection: usize,
        #[case] expected_in_flight: usize,
    ) {
        let budgets = default_memory_budgets(frame_budget);
        assert_eq!(expected_message, budgets.bytes_per_message().as_usize());
        assert_eq!(expected_connection, budgets.bytes_per_connection().as_usize());
        assert_eq!(expected_in_flight, budgets.bytes_in_flight().as_usize());
    }

    #[test]
    fn default_budgets_clamp_minimum_frame_budget() {
        let budgets = default_memory_budgets(10);
        assert_eq!(64 * 16, budgets.bytes_per_message().as_usize());
    }

    #[test]
    fn default_budgets_clamp_maximum_frame_budget() {
        let max_frame: usize = 16 * 1024 * 1024;
        let budgets = default_memory_budgets(max_frame + 1);
        assert_eq!(max_frame * 16, budgets.bytes_per_message().as_usize());
    }

    #[test]
    fn default_budgets_message_budget_aligns_with_fragmentation() {
        let frame_budget = 2048_usize;
        let budgets = default_memory_budgets(frame_budget);
        let expected = frame_budget * DEFAULT_MESSAGE_SIZE_MULTIPLIER;
        assert_eq!(
            expected,
            budgets.bytes_per_message().as_usize(),
            "per-message budget does not match fragmentation multiplier"
        );
    }
}
```

This keeps all coverage and behavior but removes the boilerplate `Result`/`io::Error` plumbing and repeated `if` patterns, making the tests shorter and easier to maintain.
</issue_to_address>

### Comment 2
<location path="docs/execplans/8-3-5-Define derived defaults based on buffer_capacity.md" line_range="68" />
<code_context>
+- `src/app/inbound_handler.rs` is currently 392 lines. Changes must keep it at
+  or below 400.
+- Use en-GB-oxendict spelling in all comments and documentation.
+- Follow the BDD 4-file pattern (feature, fixture, steps, scenarios) with
+  globally unique step text using a `derived-budget` prefix.
+- Validate with `rstest` unit tests and `rstest-bdd` v0.5.0 behavioural tests.
</code_context>
<issue_to_address>
**issue (review_instructions):** The acronym “BDD” is introduced here without being expanded on first use, which violates the requirement to define uncommon acronyms.

Please expand “BDD” on its first occurrence in this document (for example, “behaviour‑driven development (BDD)”), and then use the acronym thereafter.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.md`

**Instructions:**
Define uncommon acronyms on first use.

</details>
</issue_to_address>

### Comment 3
<location path="docs/execplans/8-3-5-Define derived defaults based on buffer_capacity.md" line_range="199" />
<code_context>
+  multiple concurrent assemblies without being so large as to be meaningless.
+  Setting `bytes_in_flight` equal to `bytes_per_connection` simplifies the
+  mental model. For the default 1024-byte frame, this yields: per_message = 16
+  KiB, per_connection = 64 KiB, in_flight = 64 KiB. Date/Author: 2026-02-26 /
+  plan phase.
+
</code_context>
<issue_to_address>
**issue (review_instructions):** The unit acronym “KiB” is used without definition, which conflicts with the requirement to define uncommon acronyms on first use.

Consider spelling out the unit on its first use, for example “16 KiB (kibibytes)”, and then using “KiB” alone afterwards.

<details>
<summary>Review instructions:</summary>

**Path patterns:** `**/*.md`

**Instructions:**
Define uncommon acronyms on first use.

</details>
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/app/builder_defaults.rs
Comment thread docs/execplans/8-3-5-Define derived defaults based on buffer_capacity.md Outdated
Comment thread docs/execplans/8-3-5-Define derived defaults based on buffer_capacity.md Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2b29008d6b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tests/fixtures/derived_memory_budgets.rs Outdated
…line tests

- Introduced `derive_budget` helper function to clean up repeated budget calculations.
- Simplified `default_memory_budgets` by using the new helper.
- Rewrote tests to use parameterized `rstest` cases for coverage and clarity.
- Removed redundant error handling in tests by using assertions.
- Improved test fixture alignment with production code by deriving fragmentation config from app instance.
- Enhanced `assert_no_connection_error` to more reliably detect server errors by awaiting server shutdown.
- Minor doc improvements and cleanup in test fixture code.

These changes improve code clarity, reduce duplication, and increase test robustness without changing functionality.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@coderabbitai coderabbitai Bot added the codex label Feb 27, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/builder_defaults.rs`:
- Around line 95-97: Replace the hard-coded literal 16 * 1024 * 1024 used to
build max_frame with the canonical codec max-frame constant (e.g.,
codec::MAX_FRAME or crate::codec::MAX_FRAME), then call
default_memory_budgets(max_frame + 1) and keep the assert using that constant
(assert_eq!(max_frame * 16, budgets.bytes_per_message().as_usize())); add an
import/path for the codec constant if needed so the test no longer duplicates
the numeric limit.

In `@tests/fixtures/derived_memory_budgets.rs`:
- Around line 271-283: The test helpers assert_connection_aborted and the second
assertion method currently await the server JoinHandle without a timeout,
risking hangs; wrap the server await (the JoinHandle taken from self.server) in
tokio::time::timeout(Duration::from_secs(2), server) inside the block_on call,
then adjust pattern matching for the nested Result/JoinError (e.g.
Ok(Ok(Ok(()))), Ok(Ok(Err(e))), Ok(Err(join_err))) and add an Err(_) branch that
returns a timed-out TestResult error message like "timed out waiting for server
task ...". Ensure you import Duration and tokio::time::timeout and replace
direct self.block_on(server)? calls with self.block_on(async { timeout(...,
server).await })? in both assert_connection_aborted and the other assertion
method.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ae19cf and 7187ac7.

📒 Files selected for processing (16)
  • docs/adr-002-streaming-requests-and-shared-message-assembly.md
  • docs/execplans/8-3-5-Define derived defaults based on buffer_capacity.md
  • docs/roadmap.md
  • docs/users-guide.md
  • src/app/builder_defaults.rs
  • src/app/frame_handling/backpressure.rs
  • src/app/frame_handling/backpressure_tests.rs
  • src/app/frame_handling/mod.rs
  • src/app/inbound_handler.rs
  • tests/features/derived_memory_budgets.feature
  • tests/fixtures/derived_memory_budgets.rs
  • tests/fixtures/mod.rs
  • tests/scenarios/derived_memory_budgets_scenarios.rs
  • tests/scenarios/mod.rs
  • tests/steps/derived_memory_budgets_steps.rs
  • tests/steps/mod.rs

Comment thread src/app/builder_defaults.rs Outdated
Comment thread tests/fixtures/derived_memory_budgets.rs
Introduce a SERVER_JOIN_TIMEOUT constant and a join_server method that
joins the server task with a bounded timeout. Refactor test helper code
in derived_memory_budgets.rs to use this method, replacing manual
blocking and result matching. Also update builder_defaults.rs tests to
use codec constants for frame length limits.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/builder_defaults.rs`:
- Around line 71-73: The test default_budgets_scale_and_use_expected_multipliers
uses raw magic numbers for multipliers and misses asserting combined derived
fields at clamp boundaries; update the test to replace literal 16 and 64 with
the multiplier constants (e.g., BYTES_PER_CONNECTION_MULTIPLIER,
BYTES_IN_FLIGHT_MULTIPLIER or their defined names) and add assertions that check
both bytes_per_connection and bytes_in_flight individually and their sum
(bytes_per_connection + bytes_in_flight) for the min and max clamp cases so the
test verifies full derived-default coverage and prevents divergence from the
policy constants.

In `@tests/fixtures/derived_memory_budgets.rs`:
- Around line 17-23: Add Rustdoc comments for the public fields on
ExplicitBudgetConfig: add a leading `///` doc comment above `per_message`
describing its purpose (per-message budget), above `per_connection` explaining
the per-connection budget, and above `in_flight` explaining the in-flight/global
budget so the public fixture API is self-describing and cargo doc friendly;
update the struct fields `per_message`, `per_connection`, and `in_flight` with
concise Rustdoc lines.
- Around line 243-250: The send_first_frames_for_range function currently
swallows errors by breaking the loop and always returning Ok(()); change it to
propagate the first encountered error (from send_first_frame or spin_runtime) by
returning that TestResult error immediately instead of breaking—i.e., when
self.send_first_frame(key, body) returns Err(e) return Err(e) (or map/propagate
the error), and likewise propagate any Err from self.spin_runtime(); keep the
TestResult return type and ensure the function returns Ok(()) only when the
entire range completes successfully.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7187ac7 and db2517d.

📒 Files selected for processing (2)
  • src/app/builder_defaults.rs
  • tests/fixtures/derived_memory_budgets.rs

Comment thread src/app/builder_defaults.rs Outdated
Comment thread tests/fixtures/derived_memory_budgets.rs
Comment thread tests/fixtures/derived_memory_budgets.rs
…d fixture robustness

- Refine default memory budgets tests to use multipliers for message, connection, and in-flight budgets instead of hardcoded values.
- Add checks for all related budget multipliers when clamping minimum and maximum frame budgets.
- Improve DerivedMemoryBudgetsWorld fixture by documenting send_first_frames_for_range behavior and tolerating intermediate frame send failures except for the initial frame.
- Add comments describing fields in ExplicitBudgetConfig struct.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/fixtures/derived_memory_budgets.rs`:
- Around line 154-158: Add a short inline comment above the hardcoded 16 in
fragmentation_from_app explaining that the multiplier mirrors
DEFAULT_MESSAGE_SIZE_MULTIPLIER (from builder_defaults) used elsewhere, so
future maintainers know this value is intentionally chosen to match that
module-private constant and should be updated in tandem if the default
multiplier changes; mention the function name fragmentation_from_app and the
constant DEFAULT_MESSAGE_SIZE_MULTIPLIER to make the intent explicit.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db2517d and 8797b78.

📒 Files selected for processing (2)
  • src/app/builder_defaults.rs
  • tests/fixtures/derived_memory_budgets.rs

Comment thread tests/fixtures/derived_memory_budgets.rs
… calculation

Added a comment in `fragmentation_from_app` clarifying that the multiplier 16 mirrors the `DEFAULT_MESSAGE_SIZE_MULTIPLIER` constant in `builder_defaults`. This helps maintain consistency if the constant changes.

Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
@leynos leynos changed the title Define derived defaults for memory budgets from buffer_capacity Define derived defaults for connection memory budgets Mar 1, 2026
@leynos leynos merged commit 594cf6a into main Mar 1, 2026
6 checks passed
@leynos leynos deleted the define-derived-defaults-buffer-capacity-m2am4x branch March 1, 2026 10:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant