PMM-15228 Improve NGINX and Auth server performance - #5658
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## PMM-15228-pmm-server-performance-metrics #5658 +/- ##
============================================================================
+ Coverage 43.47% 45.45% +1.98%
============================================================================
Files 433 549 +116
Lines 35128 45816 +10688
Branches 592 585 -7
============================================================================
+ Hits 15271 20825 +5554
- Misses 18310 23006 +4696
- Partials 1547 1985 +438
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR adds Prometheus observability to the Grafana AuthServer in pmm-managed, exposing request/cache/latency metrics and wiring the server into the Prometheus registry, plus updating the PMM Health Grafana dashboard to visualize the new signals.
Changes:
- Implemented a custom Prometheus collector in
AuthServerwith counters/gauges/histograms for auth requests, Grafana calls, cache behavior, in-flight requests, and latencies. - Registered the
AuthServercollector during pmm-managed startup so metrics are exposed automatically. - Updated the PMM Health dashboard to include panels for the new auth metrics and additional runtime/health visualizations.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
| managed/services/grafana/auth_server.go | Adds Prometheus metric descriptors/state, implements prometheus.Collector, and instruments key auth/cache/DB/Grafana code paths. |
| managed/cmd/pmm-managed/main.go | Registers the AuthServer as a Prometheus collector at startup. |
| dashboards/dashboards/PMM Health/PMM_Health.json | Adds/adjusts dashboard panels and queries to surface new auth metrics and runtime health info. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (2)
managed/services/grafana/auth_server.go:406
- The metrics label uses the raw
X-Original-Uriheader whenextractOriginalRequestfails. That header can include query strings and high-cardinality / potentially sensitive values, which is risky to expose in Prometheus labels. Use a stable, safe route label in this error path (e.g., the currentreq.URL.Pathwhich will be/auth_request).
s.incAuthRequests(req.Method, req.Header.Get("X-Original-Uri"), http.StatusBadRequest)
managed/services/grafana/auth_server.go:451
routeis recorded as the full cleaned request path (e.g./graph/api/datasources/proxy/8/in tests). That can create unbounded label cardinality and an ever-growingsync.Map(memory leak over time) when paths contain IDs or other variable segments. Consider using the matched rule prefix fromresolveRule(or another normalized route name) as theroutelabel instead of the raw path.
s.incAuthRequests(req.Method, req.URL.Path, http.StatusOK)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
managed/services/grafana/auth_server.go:258
- mAuthRequests uses the raw cleaned request path as a label and as part of the sync.Map key. For paths with variable segments (e.g. Grafana proxy routes like /graph/api/datasources/proxy//), this can create unbounded time series and unbounded in-process memory growth because entries are never evicted from the sync.Map. Consider normalizing the label (e.g., use the matched rule prefix from resolveRule / nextPrefix chain, or otherwise bucket variable segments) to keep cardinality bounded.
mAuthRequestsDesc: prom.NewDesc(
prom.BuildFQName(prometheusNamespace, prometheusSubsystem, "requests_total"),
"Total number of authentication requests.",
[]string{"method", "route", "status_code"},
nil,
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
managed/services/grafana/auth_server.go:444
routelabel is currently set to the full request path (req.URL.Path/X-Original-Uri). That can create unbounded label cardinality (IDs, arbitrary paths) and also growss.mAuthRequestswithout bound (onesync.Mapentry per unique path/method/status), which can become a memory/DoS risk over time. Consider using the matched rule prefix (fromrules/methodRules) or another bounded route identifier for the metric label instead of the raw path.
status := httpStatusForAuthError(authErr.code)
s.incAuthRequests(req.Method, req.URL.Path, status)
s.returnError(rw, status, m, l)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 20 changed files in this pull request and generated 7 comments.
Files not reviewed (2)
- managed/services/grafana/mock_access_control_test.go: Generated file
- managed/services/grafana/mock_grafana_auth_user_getter_test.go: Generated file
Suppressed comments (5)
managed/services/grafana/auth_server.go:1
errors.AsTypeis not part of the Go standard libraryerrorspackage (and this file importserrorsfrom stdlib), so this will not compile unless there's an intentional build-time replacement. Replace withvar cErr *clientError; if errors.As(err, &cErr) { ... }.
// Copyright (C) 2023 Percona LLC
managed/services/grafana/auth_server.go:347
- On
extractOriginalRequestfailure, the metric label values will beGET /auth_request(the subrequest), not the original client method/path. The previous fallback logic (usingX-Original-*headers and trimming query, optionally cleaning when possible) produced more accurate observability for these failures; consider restoring that fallback before callingincAuthRequests.
err := extractOriginalRequest(req)
if err != nil {
s.l.WithError(err).Warn("Failed to parse original request headers.")
rw.WriteHeader(http.StatusBadRequest)
s.incAuthRequests(req.Method, req.URL.Path, http.StatusBadRequest)
return
}
build/ansible/roles/nginx/files/conf.d/pmm.conf:239
- The 403 response body hardcodes
Access denied(no trailing period), while the Go AuthServer usesAccess denied.(with a period) inerrStaticAuthErrorPermissionDenied.message. This creates inconsistent client-visible error messaging (and differs from headers set on the auth subrequest). Consider aligning the NGINX static body with the Go-side message exactly, or standardizing both to the same string.
location @access_denied {
auth_request off;
default_type application/json;
return 403 '{"code":7,"error":"Access denied","message":"Access denied"}';
}
build/ansible/roles/nginx/files/conf.d/pmm.conf:232
- The JSON body is built via string interpolation without JSON-escaping
$auth_error/$auth_message. If these variables ever contain quotes/backslashes, the response can become invalid JSON (or allow JSON injection). Prefer returning a static JSON body keyed only by$status/$auth_code, or ensure values are properly escaped before embedding into JSON.
location @auth_failed {
auth_request off;
default_type application/json;
# Construct JSON payload using the extracted variables
return 401 '{"code": $auth_code, "error": "$auth_error", "message": "$auth_message"}';
}
build/ansible/roles/nginx/files/conf.d/pmm.conf:192
- Correct spelling of 'immidiatly' to 'immediately' in comment.
# Cache entry key - cached response will be used immidiatly (if it exists), otherwise
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 23 changed files in this pull request and generated 1 comment.
Files not reviewed (2)
- managed/services/grafana/mock_access_control_test.go: Generated file
- managed/services/grafana/mock_grafana_auth_user_getter_test.go: Generated file
Suppressed comments (4)
managed/services/grafana/auth_server.go:183
- Spelling/grammar: “details that is a result …” and “encoded filers” should be corrected to improve readability.
// authResult contains authentication response details that is a result of all
// authentication and authorization (including LBAC) checks.
type authResult struct {
// encoded filers to be added as proxy headers.
vmProxyFilters string
build/ansible/roles/nginx/files/conf.d/pmm.conf:193
- Typos in this block’s comments (e.g., “It incoming”, “immidiatly”, “it's response”) make the config harder to read/maintain.
# Internal location for authentication via pmm-managed/Grafana.
# It uses cache for request authentication results. It incoming request matches the
# Cache entry key - cached response will be used immidiatly (if it exists), otherwise
# a subrequest to auth backend is sent and it's response is cached.
managed/services/grafana/deps.go:25
- Spelling/grammar: “grafanaAuthUserGetter exist only …” → “exists only …”.
// grafanaAuthUserGetter exist only to make fuzzing simpler.
managed/services/grafana/auth_server.go:176
- Spelling/wording in this comment is off (“Ttl”, “validiness”). This is user-facing for maintainers and shows up in generated docs.
This issue also appears on line 179 of the same file.
// Ttl for auth response validiness in auth cache.
cacheItemTTL = 60 * time.Second
// Auth response cache cleanup interval.
cacheInvalidationInterval = 2 * cacheItemTTL
Ticket number: PMM-15228
Percona-Lab/pmm-submodules#4481
This pull request introduces significant improvements and optimizations to the NGINX configuration for PMM, focusing on authentication, caching, static asset delivery, and proxying for metrics and UI components. It also includes minor configuration updates for Go linters, mockery, and a dashboard plugin version.
Key highlights:
NGINX Configuration Improvements
Upstreams and Keepalive Optimization:
keepalive_requestsfor several upstreams (e.g.,managed-json,qan-api-json,vmproxy,nomad-server-json) to 10,000 and added new upstreams forvictoriametricsandvmalertwith tuned keepalive pools to handle higher throughput and reduce socket churn. [1] [2]Authentication and Caching Enhancements:
proxy_set_header X-Forwarded-Fordirective earlier to ensure all proxied services receive the correct client IP.Static Content and Asset Delivery:
Metrics, Alerts, and Proxy Routing:
/victoriametrics/api/v1/write.General Performance and Maintenance:
tcp_nodelayandmulti_acceptfor reduced latency and improved throughput under load. [1] [2]Other Minor Changes
Go Linter and Mockery Config:*
Dashboard Update:*
pluginVersionin the PMM Health dashboard JSON.