Skip to content

feature: allow configuration of h2c max_concurrent_streams#8384

Open
theJC wants to merge 5 commits intoopen-policy-agent:mainfrom
theJC:issue8330
Open

feature: allow configuration of h2c max_concurrent_streams#8384
theJC wants to merge 5 commits intoopen-policy-agent:mainfrom
theJC:issue8330

Conversation

@theJC
Copy link

@theJC theJC commented Feb 27, 2026

Why the changes in this PR are needed?

Addresses #8330

When using OPA with h2c enabled (--h2c), the HTTP/2 server uses Go's default MaxConcurrentStreams value of 250. For high-throughput deployments where clients need to multiplex many concurrent policy evaluation requests over a single connection, this limit becomes a bottleneck.

The 250 concurrent streams limit is enforced per-connection, and while clients can open multiple connections, this defeats some of the benefits of HTTP/2 multiplexing and increases connection overhead. Additionally some client libraries specifically do not support opening multiple connections, citing: https://httpwg.org/specs/rfc9113.html#rfc.section.9.1

What are the changes in this PR?

  • Added the ability to control the H2CMaxConcurrentStreams via parameter or config file
  • Decoupled config extraction from metricConfig to keep it isolated and used for both metrics and the new h2c max concurrent streams config
  • Added tests

Notes to assist PR review:

Nothing I can think of at the moment.

Further comments:

N/A

@netlify
Copy link

netlify bot commented Feb 27, 2026

Deploy Preview for openpolicyagent ready!

Name Link
🔨 Latest commit 91738b1
🔍 Latest deploy log https://app.netlify.com/projects/openpolicyagent/deploys/69a892e59bfa130007e8201a
😎 Deploy Preview https://deploy-preview-8384--openpolicyagent.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Contributor

@srenatus srenatus left a comment

Choose a reason for hiding this comment

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

Thanks for contributing! Code looks good, couple of comments.

More importantly: can you describe the measurable change you've seen from this code change in your environment? It would be interesting to know

  1. How you realized that the stream limit in h2c is a performance bottleneck
  2. How you verified that increasing the limit does fix the issue.

cmd/run.go Outdated
cmdParams.rt.DiagnosticAddrs = runCommand.Flags().StringSlice("diagnostic-addr", []string{}, "set read-only diagnostic listening address of the server for /health and /metric APIs (e.g., [ip]:<port> for TCP, unix://<path> for UNIX domain socket)")
cmdParams.rt.UnixSocketPerm = runCommand.Flags().String("unix-socket-perm", "755", "specify the permissions for the Unix domain socket if used to listen for incoming connections")
runCommand.Flags().BoolVar(&cmdParams.rt.H2CEnabled, "h2c", false, "enable H2C for HTTP listeners")
runCommand.Flags().Uint32Var(&cmdParams.rt.H2CMaxConcurrentStreams, "h2c-max-concurrent-streams", 0, "set maximum concurrent HTTP/2 streams per connection when H2C is enabled (0 uses the library default)")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
runCommand.Flags().Uint32Var(&cmdParams.rt.H2CMaxConcurrentStreams, "h2c-max-concurrent-streams", 0, "set maximum concurrent HTTP/2 streams per connection when H2C is enabled (0 uses the library default)")
runCommand.Flags().Uint32Var(&cmdParams.rt.H2CMaxConcurrentStreams, "h2c-max-concurrent-streams", 0, "set maximum concurrent HTTP/2 streams per connection when H2C is enabled (default: <....>")

Let's please be explicit here. Most users will now know what "library default" is supposed to mean here, or they'd need to go search to find out.

Copy link
Author

Choose a reason for hiding this comment

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

NOTE: a default of 250 is due to the actual implementation (has been that way since 2015) — it's technically consistent with "at least 100" stated in https://pkg.go.dev/golang.org/x/net/http2 — but the guarantee the library makes is "at least 100", not specifically "250".

Copy link
Author

Choose a reason for hiding this comment

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

(but updated as well in the PR)

}
}

func TestWithH2CMaxConcurrentStreams(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's remove that one, it's not adding value, I think.

Copy link
Author

Choose a reason for hiding this comment

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

removed

Comment on lines +128 to +131
// TestH2CMaxConcurrentStreams verifies that the server starts correctly and
// serves HTTP/2 requests when H2CMaxConcurrentStreams is configured. It does
// not verify that the stream limit is enforced; that would require negotiating
// concurrent streams and inspecting the HTTP/2 SETTINGS frame.
Copy link
Contributor

Choose a reason for hiding this comment

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

Makes you wonder if we need it, then, doesn't it? It doesn't test the change at hand, only insofar as that the server doesn't implode when the setting is set, which is a low bar. Do you think we can do something here to improve the test?

Copy link
Author

Choose a reason for hiding this comment

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

updated to actually peek at the http2 settings frame value

Signed-off-by: Jon Christiansen <467023+theJC@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants