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
- Lower the default
setMaxConcurrentStreams(...) to a sensible value (e.g. 100, matching common HTTP/2 server defaults).
- 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.
Problem
RestateHttpServer.DEFAULT_OPTIONSconfigures HTTP/2 with no cap on concurrent streams: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:
HttpEndpointRequestHandlerextracts the per-connection context via((HttpServerRequestInternal) request).context(), and the per-request executor re-enters that same context viactx.runOnContext(runnable)(HttpEndpointRequestHandler.java:102-105). This guarantees every stream's state-machine syscalls run on the connection's pinned event loop.With
MaxConcurrentStreams = Integer.MAX_VALUEa 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
setMaxConcurrentStreams(...)to a sensible value (e.g.100, matching common HTTP/2 server defaults).The existing
RestateHttpServer.from*(...)overloads already accept a user-suppliedHttpServerOptions, 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 forDEFAULT_OPTIONS).Context
Surfaced while investigating an intermittent
InvokerMemoryTestfailure in the Restate e2e suite. That specific failure had a different root cause (testcontainers' host-port relay), but theMaxConcurrentStreams=Integer.MAX_VALUEdefault 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.