Skip to content

Introduce PreparedApp and one-time route/middleware preparation #641

Description

@leynos

Summary

Introduce an immutable prepared-application representation that consumes WireframeApp's mutable registration state and builds route middleware chains exactly once.

This is the foundational implementation slice for ADR 012 proposal #637. It should establish the internal boundary without yet changing the server's public factory evaluation semantics; the following server-runtime issue will switch connection tasks onto the prepared root.

Problem

WireframeApp currently stores both:

  • builder inputs: handlers, middleware, callback registrations, protocol/assembler definitions, and configuration;
  • runtime cache: OnceCell<Arc<HashMap<u32, HandlerService<E>>>>.

handle_connection_result lazily initializes the route table and clones its inner Arc. When the server constructs a fresh app per connection, the cache has no opportunity to amortize its work across connections.

The route map and completed middleware chains are immutable runtime data. They should be produced by an explicit preparation transition rather than hidden inside connection handling.

Proposed design

Add an internal or initially crate-private transition shaped approximately as:

impl<S, C, E, F> WireframeApp<S, C, E, F> {
    pub(crate) async fn prepare(self) -> Result<PreparedApp<S, C, E, F>, PrepareError>;
}

PreparedApp should own:

  • HashMap<u32, HandlerService<E>> directly;
  • serializer and codec template/configuration;
  • application data;
  • lifecycle callback definitions;
  • protocol and message assembler definitions;
  • push, fragmentation, timeout, and memory-budget configuration.

Preparation should consume handlers and middleware, constructing each middleware chain once. Avoid retaining both the original handler map and a cloned route-service map after preparation unless a documented runtime need requires both.

Scope

Required

  • Define PreparedApp and any focused PrepareError type.
  • Move build_chains out of the per-connection path.
  • Consume or otherwise finalize route and middleware registrations during preparation.
  • Store the prepared route table by value, not as OnceCell<Arc<HashMap<...>>>.
  • Provide an internal compatibility path so existing tests can prepare an app before driving a connection.
  • Keep the builder's type-changing methods working before preparation.

Deferred

  • Evaluating AppFactory once during server startup.
  • Public constructor/deprecation decisions for WireframeServer.
  • Full ConnectionRuntime extraction.
  • Protocol ownership consolidation.
  • Replacing every callback/assembler Arc; that should happen after the prepared root is in use.

Acceptance criteria

  • WireframeApp has an explicit consuming preparation transition.
  • PreparedApp contains a direct prepared route table.
  • Route middleware transforms run exactly once per preparation, proven with counters.
  • Two or more connections driven from one PreparedApp reuse the same route services without rebuilding chains.
  • Duplicate route detection remains a builder-time error.
  • Route ordering and middleware ordering remain unchanged.
  • Preparation failure is typed and does not partially expose a runtime object.
  • No OnceCell<Arc<HashMap<...>>> remains in the prepared runtime representation.
  • Direct app-driving tests and testkit helpers have a clear migration path.
  • Mutation testing: untested public accessor survivors #598 is reviewed: accessors made obsolete by the prepared representation are removed or explicitly retained and tested.
  • Establish runtime ownership and task-churn baselines #639 connection-startup instrumentation records factory/transform counts before and after.

Tests

  • Prepare an app with multiple routes and middleware layers; assert transform counters equal one per layer/route as intended.
  • Drive multiple in-process connections from one prepared app; assert no additional transform calls.
  • Assert route dispatch and middleware ordering match current tests.
  • Assert a failed transform/preparation returns an error before any runtime is available, if middleware transformation can fail after the supporting error work lands.
  • Add a compile-time or type-level test that route registration is unavailable after transitioning to PreparedApp.

Design constraints

  • Do not make PreparedApp mutable behind a lock.
  • Do not clone the entire builder to preserve registrations after preparation.
  • Do not introduce a second public route-registration surface on PreparedApp.
  • Keep ADR 010's packet/transport-frame boundary unchanged.

Dependencies

This issue should land before the server-runtime and connection-runtime slices.

References

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions