Repository navigation
Fix OAP running out of memory on Envoy metrics from a large mesh - #14214
Merged
kezhenxu94 merged 1 commit intoNov 14, 2026
Merged
Conversation
PrometheusMetricConverter escaped metric names through a Guava cache capped at 1,000 entries. Envoy metric names embed the upstream cluster name, so a large mesh has far more distinct names than that: the cache kept missing, and every miss loaded and evicted under a segment lock shared by all Envoy metrics streams. Blocked streams piled up gRPC executor threads until OAP ran out of memory. Replace the cache and regex with String.replace(char, char), which needs no lock and does not allocate when there is nothing to replace.
wu-sheng
approved these changes
Nov 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix OAP running out of memory when receiving Envoy metrics from a large mesh
PrometheusMetricConverter#escapedNamereplaced.and/with_through a GuavaLoadingCachecapped at 1,000 entries. Envoy metric names embed the upstream cluster name (e.g.cluster.outbound|9080||reviews.default.svc.cluster.local.upstream_rq_total), so a large mesh has far more distinct names than the cache can hold. The cache kept missing, and every miss ran the regex and evicted an entry while holding a cache segment lock shared by all Envoy metrics streams.In a thread dump from an affected OAP, about 1,850 of 2,000
grpc-default-executorthreads were parked on that lock inescapedNamewithin about 2 minutes of startup, and 16.6% of all allocations came from the cache's loader. The threads and their pending messages piled up until the container was OOMKilled.The fix drops the cache and the regex and uses
String.replace(char, char), which needs no lock and returns the same string without allocating when there's nothing to replace.JMH comparison of the old cache and the new code (JMH 1.37, JDK 21), with Envoy-style names
cluster.outbound|9080||svc-N.ns-M.svc.cluster.local.upstream_rq_total. 200 distinct names fit in the old cache; 20,000 don't:The old cache contends even when every lookup hits (452 ns with 8 threads and 200 names): a size-capped Guava cache records each read and drains those records under the segment lock. The only case where the new code is slower is a single uncontended thread with names that fit in the cache (+12 ns, about +90 B per call), which is small next to the rest of the per-metric conversion. The benchmark reuses the same name strings, so their hash codes are cached; in OAP each name is a fresh string from protobuf, which the old cache had to hash on every lookup.
Benchmark source
CHANGESlog.