fix: reclaim tenant metrics on pool close - #823
Conversation
|
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 (7)
🚧 Files skipped from review as they are similar to previous changes (7)
📝 WalkthroughWalkthroughThis PR adds selective deletion of tenant request counters, one-time completion recording, authentication middleware wiring, and cleanup during tenant pool entry closure. Tests verify that cleanup removes request counters without removing in-flight metrics. ChangesTenant Request Counter Cleanup
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant AuthMiddleware
participant RequestState
participant Registry
participant TenantPool
AuthMiddleware->>RequestState: defer recordTenantHTTPRequestAtCompletion
RequestState->>RequestState: observe registers recorder callback
RequestState->>Registry: record tenant request counter once at completion
TenantPool->>Registry: DeleteTenantRequestCounters on closeEntry
Registry->>Registry: DeleteCounterByLabel removes matching series
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 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 |
6b563b0 to
a701c87
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5fae274dc5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
This PR primarily bounds per-process Prometheus tenant-metric cardinality by tying tenant-scoped metric series to the lifecycle of the local tenant backend cache (deleting tenant series when the final cached entry closes, while avoiding deletion of a replacement entry’s series). It also introduces a small HTTP auth response behavior update for provisioning-related tenant statuses.
Changes:
- Reclaim tenant-scoped counters/histograms/gauges on
tenant.Pool.closeEntry, while holding the pool mutex to avoid deleting metrics for a concurrently inserted replacement entry. - Update tenant-pool tests to assert metric cleanup on close and preservation when a replacement entry is present.
- Add a retryable JSON error helper (
Retry-After) and adjust auth middleware responses for pending/provisioning/failed tenant statuses, with new test coverage; add accompanying design/plan docs.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/tenant/pool.go | Deletes tenant metric series on entry close when no active cache entry exists for that tenant (mutex-serialized replacement detection). |
| pkg/tenant/pool_test.go | Adds/updates tests to validate metric cleanup on close and preservation for replacement entries. |
| pkg/server/server.go | Introduces errJSONRetryable helper (adds Retry-After + 503 JSON error). |
| pkg/server/auth.go | Uses retryable errors for pending/provisioning and changes TenantFailed handling (status code change). |
| pkg/server/auth_test.go | Adds coverage for auth responses (status codes and Retry-After presence). |
| docs/superpowers/specs/2026-07-31-tenant-pool-metrics-lifecycle-design.md | Documents intended metric lifecycle and concurrency contract. |
| docs/superpowers/plans/2026-07-31-tenant-pool-metrics-cleanup.md | Implementation plan for tenant-pool metric cleanup. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Review at
|
a701c87 to
f0480f6
Compare
|
Addressed in
Validation: |
Re-review at
|
mornyx
left a comment
There was a problem hiding this comment.
Review summary
Verdict: Approve
Reviewed in an isolated worktree at f0480f6. Focused fix for process-local Prometheus cardinality of drive9_tenant_requests_total after #802 stopped reclaiming series on cache eviction. Local tests passed:
go test ./pkg/tenant -run '^TestCloseEntryCleansTenantRequestCounters$'go test ./pkg/metrics
CI green. The design matches the stated contract: request counters may reset on cache eviction and must be queried with rate() / increase(), while durable deleting/deleted cleanup still uses full DeleteTenantCounters + cross-pod WorkMetricsCleanup.
Correctness
- Scope is intentionally narrow —
DeleteTenantRequestCountersonly touches thedrive9_tenant_requests_totalcounter map for onetenant_id. Gauges/histograms/SSE/storage are left alone. Request duration histogram is already not labeled bytenant_id(onlysurface/status_class), so it is not the cardinality hot spot. - Placement — called from
closeEntry, which runs when an entry is retired andrefshit 0 (idle eviction, invalidate after last release, replace, shutdown). Not under the pool mutex; only the metrics registry lock is taken for a single counter instrument — good. - In-flight request order —
observeis outer:next.ServeHTTPcompletes (authdefer release()maycloseEntry+ delete counters) thenrecordTenantHTTPRequestincrements the counter again. So the completing request is not lost; the series is recreated from 0 with the last sample. Compatible withrate()/increase(). Historical pre-eviction series intentionally disappear. - Pin safety —
removeLockedWithoutMetricsreturns the entry for immediate close only whenrefs == 0; otherwise close is deferred to the lastreleaseEntry. No close while pins remain. - Lifecycle cleanup unchanged — durable delete path still calls full
DeleteTenantCountersvia outbox / work mask. - Test — asserts request counter gone and inflight gauge retained after
closeEntry.
Non-blocking notes
- Codex P2 about “delete before observe” is real order-wise but not a functional loss of the completing request; series reset is the product intent. No code change needed if dashboards use
rate()/increase(). - Optional unit test for
Registry.DeleteCounterByLabel(name-scoped delete) to lock the O(series for one counter) contract. - Inflight gauges with
tenant_idcan still linger at last value after eviction; out of scope here and probably lower cardinality, but worth watching if gauge series also grow.
No blockers. Approved.
f0480f6 to
f7dd66e
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@pkg/tenant/pool_test.go`:
- Around line 1896-1902: Update the test helper metricHasTenantID to accept
*testing.T as its first parameter and call t.Helper() immediately at the start;
then update both metricHasTenantID call sites to pass the current testing
handle.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: cd61e2cc-0780-46c4-8268-772a87a40b19
📒 Files selected for processing (7)
pkg/metrics/operations.gopkg/metrics/registry.gopkg/server/auth.gopkg/server/instrumentation.gopkg/server/instrumentation_test.gopkg/tenant/pool.gopkg/tenant/pool_test.go
f7dd66e to
409a892
Compare
Summary
drive9_tenant_requests_totaltenant series when a local tenant backend cache entry closesWhy this revisits #802
#802 deliberately stopped deleting metrics on pool eviction to preserve a never-reset tenant request counter. Production Tencent data shows that policy leaves roughly 20k unique request-counter tenants per pod while the local backend cache contains only hundreds of tenants. The agreed metric contract is now that
tenant_requests_totalmay reset after cache eviction; dashboards must userate()orincrease()rather than treat the raw process-local counter as a durable tenant request ledger.This PR deliberately does not restore #773/#802-era broad
DeleteTenantCountersbehavior on cache close. Durable deleting/deleted lifecycle transitions continue to useDeleteTenantCountersand the cross-podWorkMetricsCleanupoutbox.Validation
go test ./pkg/server -run '^TestRecordTenantHTTPRequestAtCompletionDoesNotRecreateCleanedCounter$' -count=1go test ./pkg/server ./pkg/metrics ./pkg/tenant -count=1make testwas run earlier; it is not fully green due to unrelated existing failurecmd/drive9/cli.TestCtxImport_RejectsWorldReadableFile(mode-0644 JWT file expected to be rejected but received nil).Summary by CodeRabbit
Bug Fixes
Tests