grpcutil: bound stream metrics peer IP label lifecycle#11042
Conversation
Track active streams per request and peer IP, and delete histogram children after the last matching stream ends so disconnected targets do not accumulate. Serialize child creation and deletion with reference counts to keep concurrent streams from observing orphaned histograms. Signed-off-by: JmPotato <github@ipotato.me>
7e61520 to
0331494
Compare
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughgRPC stream send-duration metrics now use a reference-counted collector that removes request/target histogram series after the last matching stream ends. ChangesStream metrics lifecycle
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant StreamHandler
participant NewMetricsStream
participant StreamSendDurationCollector
participant PrometheusHistogram
StreamHandler->>NewMetricsStream: create metrics stream
NewMetricsStream->>StreamSendDurationCollector: acquire(request, target)
StreamSendDurationCollector->>PrometheusHistogram: bind labeled observer
StreamHandler->>NewMetricsStream: send messages
NewMetricsStream->>StreamSendDurationCollector: release on context cancellation
StreamSendDurationCollector->>PrometheusHistogram: delete labels when last stream releases
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
/retest |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #11042 +/- ##
==========================================
- Coverage 79.25% 79.22% -0.04%
==========================================
Files 541 542 +1
Lines 76037 76124 +87
==========================================
+ Hits 60262 60306 +44
- Misses 11534 11555 +21
- Partials 4241 4263 +22
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
Rename the MetricsStream collector parameter and collapse test metric helpers so lifecycle coverage stays the same with less boilerplate. Signed-off-by: JmPotato <github@ipotato.me>
What problem does this PR solve?
Issue Number: Close #11040
gRPC stream duration metrics retain a histogram child for every historical
(request, peer IP)label pair until the process restarts, causing stale series to accumulate in/metricsafter clients disconnect.What is changed and how does it work?
Performance
These are package-level microbenchmarks of the metrics wrapper, not end-to-end gRPC benchmarks.
Environment: Apple M4 (10 cores, 24 GiB), macOS 27.0 (
darwin/arm64), Go 1.26.5,GOMAXPROCS=10. The base revision isf7db42521and the PR revision is033149448. Each benchmark used a fixed iteration count and 10 independent runs, summarized withbenchstat. The common benchmark body was identical between revisions; revision-specific adapters only account for the base revision having no release state to await.Sendp=0.190)NewMetricsStreamThe steady-state
Sendpath has no measurable regression, which is consistent with its source code being unchanged. The shared-label setup and release benchmarks isolate lifecycle bookkeeping: all streams use the same(request, target)key, and only the final cancellation deletes the histogram child. Their combined incremental cost is approximately 0.79 µs and 544 B with 6 allocations per stream lifecycle.The single-owner benchmark represents short-lived stream churn: every PR iteration transitions the refcount from 1 to 0, deletes the child, and recreates it on the next iteration. Its additional cost is approximately 1.99 µs, 2.1 KiB, and 22 allocations per complete stream lifecycle. This remains a one-time lifecycle cost rather than a per-message cost.
For the PR revision, cancellation benchmarks wait for the asynchronous
context.AfterFuncrelease to complete, so the reported values include goroutine scheduling and cleanup completion. The gRPC teardown path itself does not synchronously wait for this cleanup.Limitations: These microbenchmarks do not measure complete RPC latency or throughput, concurrent reconnect storms, Prometheus scrape contention, or the expected memory and scrape-size reduction from removing stale series.
Benchmark semantics and commands
Send: the wrapper and observer are initialized before timing; the timed region only callsSend.NewMetricsStream.DeleteLabelValues.Results were compared with
golang.org/x/perf/cmd/benchstatatv0.0.0-20260709024250-82a0b07e230d.Check List
Tests
Side effects
NewGRPCStreamSendDurationnow returns*StreamSendDurationCollector, andNewMetricsStreamaccepts that collector instead of*prometheus.HistogramVec.Related changes
release-nextgen-202603Release note
Summary by CodeRabbit
Enhancements
Tests