Skip to content

client/resource_group: expose RU consumption by request source - #10588

Open
YuhaoZhang00 wants to merge 12 commits into
tikv:masterfrom
YuhaoZhang00:rg-request-source-metrics
Open

client/resource_group: expose RU consumption by request source#10588
YuhaoZhang00 wants to merge 12 commits into
tikv:masterfrom
YuhaoZhang00:rg-request-source-metrics

Conversation

@YuhaoZhang00

@YuhaoZhang00 YuhaoZhang00 commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: close #10634

Tracking issue: pingcap/tidb#64339
Related PR: tikv/client-go#1929

pd/client accounts RU consumption in the resource-group controller, but it does not expose a breakdown by request source. This makes it difficult to attribute RU usage to user queries, DDL, stats, and other internal activities.

This PR exposes RRU and WRU consumption by request source through the existing pd/client accounting flow.

What is changed and how does it work?

  • Optionally read RequestSource() string from controller.RequestInfo. Requests without this provider use an empty request_source.
  • Add resource_manager_client_request_ru_total{resource_group, request_source, type, direction}:
    • type: rru or wru
    • direction: consume or refund
  • Record deltas at the same request and response boundaries as the controller's RU accounting. Negative deltas are recorded as positive refund values, so net RU is consume - refund.
  • Lazily cache metric handles on the request path and clean them up with the resource group or controller.

This metric may create up to the following number of Prometheus time series within one client process if every request source occurs in every active resource group:

active resource groups * distinct request_source values * 2 RU types * 2 directions

Metric handles are created lazily, so only observed non-zero label combinations produce series. However, this PR does not cap the number of request_source values; deployments with many resource groups or high-cardinality request sources may therefore create a large number of series.

Dependency on client-go

This PR works independently because RequestSource() is optional. However, attributing TiDB/client-go traffic to its actual source requires tikv/client-go#1929. Without that plumbing, requests are recorded with an empty request_source.

The metric becomes effective for client-go after it consumes a pd/client version containing this PR.

Check List

Tests:

  • Unit tests cover request/response accounting, consume/refund deltas, failed-write payback, concurrent metric creation, and lifecycle cleanup.
  • cd client && make static

Manual end-to-end test:

Real INSERT and SELECT statements produced:

request_source="external_Insert", type="wru", direction="consume"
request_source="leader_external_Select", type="rru", direction="consume"

After repeating the workload, the counters increased as expected:

external_Insert:        1.8484375 → 5.44765625
leader_external_Select: 0.5092324625651041 → 0.9982272949218749

Requests that bypass RU accounting are intentionally absent from this metric.

Release note

None

@ti-chi-bot ti-chi-bot Bot added do-not-merge/needs-linked-issue do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. labels Apr 9, 2026
@coderabbitai

coderabbitai Bot commented Apr 9, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Per-request-source RU/WRU metrics were added: controllers now cache per-group request-source metric state, record per-request-source RU/WRU deltas to a new Prometheus counter, RequestInfo requires RequestSource(), and global/group cleanup removes and unregisters those per-source metrics.

Changes

Cohort / File(s) Summary
Global Controller
client/resource_group/controller/global_controller.go
Added requestSourceStates cache; reset RequestSourceRUCounter on controller shutdown; pass per-group request-source state into newGroupCostController; ensure request-source state is cleaned on group deletion/tombstone and periodic cleanup paths.
Group Controller & Metrics Integration
client/resource_group/controller/group_controller.go, client/resource_group/controller/metrics/metrics.go
Wired shared requestSourceMetricsState into groupMetricsCollection; added lazy per-request-source counter creation, addRequestSourceRU to record RU/WRU deltas, and cleanup() to delete labeled series; introduced RequestSourceRUCounter CounterVec and registered it; updated failed-request label constant rename (errTypetypeLabel).
Model & Tests Helpers
client/resource_group/controller/model.go, client/resource_group/controller/testutil.go
Extended RequestInfo interface with RequestSource() string; updated TestRequestInfo to include requestSource field and implement the method.
Tests
client/resource_group/controller/request_source_metrics_test.go, client/resource_group/controller/group_controller_test.go, client/resource_group/controller/testutil.go
Added comprehensive tests exercising per-source metrics caching, recording, cleanup, tombstone/revive scenarios; updated test call sites to pass newRequestSourceMetricsState(...) to newGroupCostController.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant GC as GroupController
  participant Glo as GlobalController
  participant P as Prometheus
  Client->>GC: send request (RequestInfo with RequestSource)
  GC->>GC: compute RU/WRU delta
  GC->>GC: getOrCreateRequestSourceMetricsState(resource_group, request_source)
  GC->>P: increment RequestSourceRUCounter{resource_group, request_source, type}
  GC->>Client: respond
  Note over Glo,GC: On shutdown / cleanup
  Glo->>GC: trigger cleanup/tombstone
  GC->>P: delete labeled series via cleanup()
  GC->>Glo: remove cached request-source state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

size/L, type/development, lgtm

Suggested reviewers

  • JmPotato
  • rleungx
  • disksing

Poem

🐇
I count the hops of requests and dew,
Per-source tallies made anew.
Maps held warm, then swept away—
Metrics bloom, then sleep by day.
A rabbit nods; the counters play.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.05% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: exposing RU consumption by request source.
Description check ✅ Passed The description covers the problem, implementation, tests, issue link, and release note, with only optional template sections left sparse.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ti-chi-bot

ti-chi-bot Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Hi @YuhaoZhang00. Thanks for your PR.

I'm waiting for a tikv member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@ti-chi-bot ti-chi-bot Bot added contribution This PR is from a community contributor. dco-signoff: no Indicates the PR's author has not signed dco. needs-ok-to-test Indicates a PR created by contributors and need ORG member send '/ok-to-test' to start testing. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Apr 9, 2026
@YuhaoZhang00 YuhaoZhang00 changed the title tso, server: add debug logs for TSO sync, closure, and forwarding paths client/resource_group: cache request source RU metrics Apr 9, 2026
@YuhaoZhang00
YuhaoZhang00 force-pushed the rg-request-source-metrics branch from 1dd52fd to ebd4f63 Compare April 9, 2026 03:23
@ti-chi-bot ti-chi-bot Bot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Apr 9, 2026
Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
@YuhaoZhang00
YuhaoZhang00 force-pushed the rg-request-source-metrics branch from ebd4f63 to 492976a Compare April 9, 2026 03:29
@ti-chi-bot ti-chi-bot Bot added dco-signoff: yes Indicates the PR's author has signed the dco. and removed dco-signoff: no Indicates the PR's author has not signed dco. labels Apr 9, 2026
@codecov

codecov Bot commented Apr 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.76923% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.31%. Comparing base (39b6220) to head (34800be).

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #10588      +/-   ##
==========================================
+ Coverage   79.25%   79.31%   +0.06%     
==========================================
  Files         541      541              
  Lines       76037    76154     +117     
==========================================
+ Hits        60262    60405     +143     
+ Misses      11534    11507      -27     
- Partials     4241     4242       +1     
Flag Coverage Δ
unittests 79.31% <90.76%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@YuhaoZhang00
YuhaoZhang00 marked this pull request as ready for review April 9, 2026 05:00
@ti-chi-bot ti-chi-bot Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Apr 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
client/resource_group/controller/request_source_metrics_test.go (1)

24-35: Consider increasing channel buffer or using unbuffered pattern for robustness.

The channel buffer of 8 could cause the goroutine to block if the collector produces more metrics than the buffer size before the main routine starts consuming. While this is unlikely in controlled test scenarios, a more robust pattern would be to use an unbuffered channel and start consuming immediately, or increase the buffer size.

♻️ Suggested improvement for robustness
 func collectorMetricCount(collector prometheus.Collector) int {
-	ch := make(chan prometheus.Metric, 8)
+	ch := make(chan prometheus.Metric, 128)
 	go func() {
 		collector.Collect(ch)
 		close(ch)
 	}()
 	count := 0
 	for range ch {
 		count++
 	}
 	return count
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@client/resource_group/controller/request_source_metrics_test.go` around lines
24 - 35, In collectorMetricCount, avoid the fixed small buffered channel which
can block if collector emits >8 metrics; change ch := make(chan
prometheus.Metric, 8) to either an unbuffered channel (ch := make(chan
prometheus.Metric)) so the main goroutine immediately consumes while the
goroutine runs, or increase the buffer to a safely large value (e.g., 256/1024)
to prevent blocking; ensure this change is applied in the collectorMetricCount
function that calls collector.Collect(ch).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@client/resource_group/controller/request_source_metrics_test.go`:
- Around line 24-35: In collectorMetricCount, avoid the fixed small buffered
channel which can block if collector emits >8 metrics; change ch := make(chan
prometheus.Metric, 8) to either an unbuffered channel (ch := make(chan
prometheus.Metric)) so the main goroutine immediately consumes while the
goroutine runs, or increase the buffer to a safely large value (e.g., 256/1024)
to prevent blocking; ensure this change is applied in the collectorMetricCount
function that calls collector.Collect(ch).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bae52b87-fccd-416c-a4c3-3b5c37206d41

📥 Commits

Reviewing files that changed from the base of the PR and between b21a183 and 492976a.

📒 Files selected for processing (6)
  • client/resource_group/controller/global_controller.go
  • client/resource_group/controller/group_controller.go
  • client/resource_group/controller/metrics/metrics.go
  • client/resource_group/controller/model.go
  • client/resource_group/controller/request_source_metrics_test.go
  • client/resource_group/controller/testutil.go

@YuhaoZhang00

Copy link
Copy Markdown
Contributor Author

/cc @JmPotato ptal

@ti-chi-bot
ti-chi-bot Bot requested a review from JmPotato April 9, 2026 05:17
@ti-chi-bot

ti-chi-bot Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

@YuhaoZhang00: GitHub didn't allow me to request PR reviews from the following users: ptal.

Note that only tikv members and repo collaborators can review this PR, and authors cannot review their own PRs.

Details

In response to this:

/cc @JmPotato ptal

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@YuhaoZhang00

Copy link
Copy Markdown
Contributor Author

/release-note-none

@ti-chi-bot ti-chi-bot Bot added release-note-none Denotes a PR that doesn't merit a release note. and removed do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. labels Apr 9, 2026
func (mc *groupMetricsCollection) cleanupRequestSourceMetrics(resourceGroupName string) {
mc.sourceMetricsMu.Lock()
defer mc.sourceMetricsMu.Unlock()
for requestSource := range mc.sourceMetrics {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will it leak if the getOrCreateRequestSourceMetrics create a new one?

@YuhaoZhang00 YuhaoZhang00 Apr 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No extra leak from this cache:

1. These cached request-source metrics are cleaned up when the resource group is cleaned up (cleanupRequestSourceMetrics() called), so they do not stay around forever.

2. The request_source cardinality is also bounded in practice. In TiDB/client-go it currently comes from a small set of hardcoded values (< 100), so we do not expect it to grow uncontrollably.

If the concern is about concurrency issue, all sourceMetrics operations are wrapped by mutex locks.

@YuhaoZhang00

Copy link
Copy Markdown
Contributor Author

/hold

Comment thread client/resource_group/controller/group_controller.go Outdated
… RU metrics

The per-source RU counter previously exported the request-side delta on
onRequestWait and the response-side delta on onResponse. On a failed write
AfterKVRequest emits a negative WRU via payBackWriteCost, which was dropped
by the `if WRU > 0` clamp in addRequestSourceRU. The controller's own
gc.mu.consumption did receive the payback, so the per-source counter
permanently drifted above controller consumption proportional to the
failed-write rate — biasing attribution against retry-prone sources.

Defer the per-source export to response time and pass the round-trip net
`count` (already built for store/global penalty bookkeeping). For a failed
write, count = AfterKVRequest delta + BeforeKVRequest write cost, which
yields the residual cost (per-batch base + per-replica overhead) that the
controller actually charges. The counter therefore tracks gc.mu.consumption
on success, failure, and read paths.

Add TestFailedWriteMatchesControllerConsumption to pin the contract over
mixed success/failure traffic.

Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
@ti-chi-bot ti-chi-bot Bot added the lgtm label May 9, 2026
@ti-chi-bot

ti-chi-bot Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: okJiang, rleungx

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot Bot removed the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label May 9, 2026
@ti-chi-bot

ti-chi-bot Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

[LGTM Timeline notifier]

Timeline:

  • 2026-04-14 06:36:50.813430756 +0000 UTC m=+1456616.018790803: ☑️ agreed by rleungx.
  • 2026-05-09 03:50:56.545099823 +0000 UTC m=+498929.418449796: ☑️ agreed by okJiang.

@okJiang

okJiang commented May 19, 2026

Copy link
Copy Markdown
Member

@YuhaoZhang00 please fix the ci

@YuhaoZhang00 YuhaoZhang00 changed the title client/resource_group: cache request source RU metrics client/resource_group: expose RU consumption by request source Jul 21, 2026
@YuhaoZhang00

YuhaoZhang00 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Cross-repository dependency: for this metric to take effect in TiDB/client-go, client-go must propagate the request source and implement RequestSource() on its resourcecontrol.RequestInfo (see tikv/client-go#1929). A consumer that upgrades to this pd/client version without that method will no longer satisfy controller.RequestInfo; without a meaningful propagated source, the metric cannot provide the intended per-source attribution.

Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
@YuhaoZhang00

Copy link
Copy Markdown
Contributor Author

/retest

@YuhaoZhang00

YuhaoZhang00 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

/hold

hold temporarily for local (re)test.

@ti-chi-bot ti-chi-bot Bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jul 21, 2026
@YuhaoZhang00

Copy link
Copy Markdown
Contributor Author

/retest

@JmPotato JmPotato left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address the inline lifecycle issue.

defer s.mu.Unlock()
s.closed = true
for key := range s.items {
metrics.RequestSourceRUCounter.DeleteLabelValues(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RequestSourceRUCounter is process-global, so different controllers can cache the same series. Deleting it here leaves other controllers with orphaned handles whose later Add calls are no longer exported. Please make deletion ownership-aware (for example, delete only after the last owner) and add a two-controller test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe next-gen is designed to have only one active ResourceGroupsController per process in steady state (correct me if I'm wrong!).

However, a very small overlap window may still exist during bootstrap, upgrade, or controller handoff while the old controller is shutting down and the new one is starting, and I'll try to fix this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand your point: in the current way TiDB is used, a process has only a single ResourceGroupsController. However, this is not a hard constraint, and it may change in future development, which is why I have some concerns. Still, the PR can be merged as is. For this issue, I think we should leave a TODO or at least keep it in mind, as we may encounter potential pitfalls down the road.

@YuhaoZhang00 YuhaoZhang00 Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will open a new issue to note this down -> #11080

Signed-off-by: Yuhao Zhang <yhzhang00@outlook.com>
@YuhaoZhang00
YuhaoZhang00 requested a review from JmPotato July 31, 2026 08:23
@YuhaoZhang00

Copy link
Copy Markdown
Contributor Author

/unhold

@ti-chi-bot ti-chi-bot Bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jul 31, 2026
@YuhaoZhang00

Copy link
Copy Markdown
Contributor Author

/retest

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved contribution This PR is from a community contributor. dco-signoff: yes Indicates the PR's author has signed the dco. lgtm ok-to-test Indicates a PR is ready to be tested. release-note-none Denotes a PR that doesn't merit a release note. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

client/resource_group: cache request source RU metrics in pd/client

4 participants