transport: add opt-in env var to never-index high-cardinality headers… - #9378
transport: add opt-in env var to never-index high-cardinality headers…#9378doroshenkosa wants to merge 1 commit into
Conversation
… in HPACK The client transport shares a single HPACK encoder across every RPC on a connection, so the encoder's dynamic table state persists between requests. Indexing a header whose value differs on (almost) every RPC - a trace or request identifier, for example - is pure churn: the dynamic-table entry it adds can never be matched by a later request, yet every RPC pays for the insert and, once the 4KB table fills, for evicting the oldest entry, which can also push out genuinely reusable entries such as content-type and :authority. Add a comma-separated, case-insensitive environment variable, GRPC_GO_EXPERIMENTAL_HPACK_NEVER_INDEX_HEADERS, listing outgoing header names that the client encodes as HPACK "never indexed" literals (RFC 7541 6.2.3) instead of adding them to the dynamic table. It applies across every source of outgoing headers (outgoing-context metadata, AppendToOutgoingContext, transport metadata and per-RPC credentials) and defaults to empty, so the wire format is unchanged unless the feature is explicitly opted into. This is a CPU-for-bandwidth trade-off and is not a win for every workload: a never-indexed header keeps its name out of the table too, so the name is re-sent as a literal on each RPC rather than referenced by a one-byte index. BenchmarkHPACKNeverIndex measures both sides (sec/op and bytes/rpc). The variable is meant for CPU-bound deployments to point at the specific high-cardinality headers they send; it should not list headers whose value repeats, such as a stable authorization token. RELEASE NOTES: * transport: add the experimental GRPC_GO_EXPERIMENTAL_HPACK_NEVER_INDEX_HEADERS environment variable to keep listed high-cardinality headers out of the HPACK dynamic table
|
Friendly ping - would appreciate a maintainer review when you have a chance. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #9378 +/- ##
==========================================
- Coverage 87.47% 87.45% -0.02%
==========================================
Files 425 425
Lines 30303 30321 +18
==========================================
+ Hits 26508 26518 +10
- Misses 3794 3803 +9
+ Partials 1 0 -1
🚀 New features to boost your workflow:
|
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces the GRPC_GO_EXPERIMENTAL_HPACK_NEVER_INDEX_HEADERS environment variable, allowing users to configure a set of outgoing header names that the client transport encodes as HPACK 'never indexed' literals instead of adding them to the dynamic table. This helps optimize CPU usage at the cost of bandwidth for high-cardinality headers like trace or request identifiers. The changes include the environment variable parsing logic, integration into the HTTP/2 client header creation, comprehensive unit tests, and a benchmark to measure the trade-offs. There are no review comments, so I have no feedback to provide.
|
Hey @doroshenkosa , thank you for making this change! I am discussing this internally with the other maintainers to align on the best approach, and I will get back to you soon. |
… in HPACK
The client transport shares a single HPACK encoder across every RPC on a connection, so the encoder's dynamic table state persists between requests. Indexing a header whose value differs on (almost) every RPC - a trace or request identifier, for example - is pure churn: the dynamic-table entry it adds can never be matched by a later request, yet every RPC pays for the insert and, once the 4KB table fills, for evicting the oldest entry, which can also push out genuinely reusable entries such as content-type and :authority.
Add a comma-separated, case-insensitive environment variable, GRPC_GO_EXPERIMENTAL_HPACK_NEVER_INDEX_HEADERS, listing outgoing header names that the client encodes as HPACK "never indexed" literals (RFC 7541 6.2.3) instead of adding them to the dynamic table. It applies across every source of outgoing headers (outgoing-context metadata, AppendToOutgoingContext, transport metadata and per-RPC credentials) and defaults to empty, so the wire format is unchanged unless the feature is explicitly opted into.
This is a CPU-for-bandwidth trade-off and is not a win for every workload: a never-indexed header keeps its name out of the table too, so the name is re-sent as a literal on each RPC rather than referenced by a one-byte index. BenchmarkHPACKNeverIndex measures both sides (sec/op and bytes/rpc). The variable is meant for CPU-bound deployments to point at the specific high-cardinality headers they send; it should not list headers whose value repeats.
RELEASE NOTES: