Skip to content

sdk-http-vertx: cap and expose MaxConcurrentStreams (default Integer.MAX_VALUE concentrates fan-in onto one event-loop) #615

@tillrohrmann

Description

@tillrohrmann

Problem

RestateHttpServer.DEFAULT_OPTIONS configures HTTP/2 with no cap on concurrent streams:

private static final HttpServerOptions DEFAULT_OPTIONS =
    new HttpServerOptions()
        .setInitialSettings(new Http2Settings().setMaxConcurrentStreams(Integer.MAX_VALUE));

Vert.x pins each accepted HTTP/2 connection to one event-loop thread for the connection's lifetime, and all streams multiplexed on that connection serialize on that thread. Evidence in this codebase:

  • HttpEndpointRequestHandler extracts the per-connection context via ((HttpServerRequestInternal) request).context(), and the per-request executor re-enters that same context via ctx.runOnContext(runnable) (HttpEndpointRequestHandler.java:102-105). This guarantees every stream's state-machine syscalls run on the connection's pinned event loop.
  • Vert.x's standard HttpServer model (see Advanced Vert.x Guide — Threading) pins each accepted connection to a single event-loop context for its lifetime.

With MaxConcurrentStreams = Integer.MAX_VALUE a single HTTP/2 connection can multiplex arbitrarily many concurrent streams, all of which then serialize on one thread. Under load this concentrates fan-in rather than spreading it across the available event loops. Most other HTTP/2 server defaults are far smaller for this reason (Node's http2 default: 100; gRPC-Java default: ~100).

Proposal

  1. Lower the default setMaxConcurrentStreams(...) to a sensible value (e.g. 100, matching common HTTP/2 server defaults).
  2. Expose the knob on the endpoint builder so users who genuinely want unbounded multiplexing per connection can opt in.

The existing RestateHttpServer.from*(...) overloads already accept a user-supplied HttpServerOptions, so opting in to a higher cap is already possible — but most users won't pass custom options, and the current shipped default is the surprising one.

Files

  • sdk-http-vertx/src/main/java/dev/restate/sdk/http/vertx/RestateHttpServer.java (line 46-48 for DEFAULT_OPTIONS).

Context

Surfaced while investigating an intermittent InvokerMemoryTest failure in the Restate e2e suite. That specific failure had a different root cause (testcontainers' host-port relay), but the MaxConcurrentStreams=Integer.MAX_VALUE default came up repeatedly as a separate hardening item: nothing in the SDK protects users from a peer fanning N streams onto a single event-loop thread.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions