Skip to content

client: fix resource group config watch recovery#10883

Merged
ti-chi-bot[bot] merged 10 commits into
tikv:masterfrom
bufferflies:bugfix/too_many_watch-10882
Jun 12, 2026
Merged

client: fix resource group config watch recovery#10883
ti-chi-bot[bot] merged 10 commits into
tikv:masterfrom
bufferflies:bugfix/too_many_watch-10882

Conversation

@bufferflies

@bufferflies bufferflies commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: Close #10882, Close #9895

The resource group controller can retry controller config watches from a stale revision after the watch stream is closed because the requested revision has been compacted. This can cause repeated failed watch attempts / noisy watch requests and delay applying updated controller config.

What is changed and how does it work?

Propagate meta storage watch responses with compact revision information to the PD client.

When a controller config watch receives a compacted revision response, advance the cached config revision before recreating the watch so the retry does not use the same stale revision.

Add an integration test that injects a compacted watch revision and verifies the resource group controller does not repeatedly watch the stale revision while still applying updated RU version config.

Check List

Tests

  • Integration test

Side effects

  • Increased code complexity

Related changes

  • Need to cherry-pick to the release branch

Release note

Fix the issue that resource group controller config watches can be retried with a stale compacted revision.

Summary by CodeRabbit

  • New Features

    • Watch API now returns a richer response bundling events with compact-revision metadata; consumers updated accordingly.
  • Bug Fixes

    • Improved watch-stream error detection, reporting and logging; handles compacted revisions and unexpected watch closures more robustly.
  • Tests

    • Updated unit and integration tests and mocks to the new watch response type; added an integration test verifying watch invocation behavior.

Signed-off-by: tongjian <1045931706@qq.com>
Signed-off-by: tongjian <1045931706@qq.com>
Signed-off-by: tongjian <1045931706@qq.com>
Signed-off-by: tongjian <1045931706@qq.com>
Signed-off-by: tongjian <1045931706@qq.com>
@ti-chi-bot ti-chi-bot Bot added release-note Denotes a PR that will be considered when it comes time to generate release notes. do-not-merge/needs-triage-completed dco-signoff: yes Indicates the PR's author has signed the dco. labels Jun 11, 2026
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Refactors meta-storage watches to emit a structured metastorage.WatchResponse (Events + CompactRevision) and updates client producers, consumers, server handling, controller logic, and tests to use the new shape.

Changes

Meta Storage Watch API Refactoring

Layer / File(s) Summary
Watch API contract and type definitions
client/clients/metastorage/client.go, client/resource_manager_client.go
Introduces exported WatchResponse (Events + CompactRevision) and updates Watch signatures to return chan *WatchResponse.
Meta storage client watch implementation
client/meta_storage_client.go
Inner and public client Watch now build and emit metastorage.WatchResponse; detect DATA_COMPACTED headers to set CompactRevision, add logging, and respect context cancellation.
Resource group controller watch integration
client/resource_group/controller/global_controller.go
Controller watch loops now consume *metastorage.WatchResponse, iterate resp.Events, apply staleRevision failpoint, and advance cfgRevision from resp.CompactRevision.
Server-side watch loop with failpoint support
pkg/mcs/metastorage/server/grpc_service.go
Adds a failpoint to trigger manager.Compact at watch start for tests; refactors watch error handling to send error-bearing WatchResponse and return the captured error.
Test mocks and fake clients
client/resource_group/controller/global_controller_test.go, client/resource_manager_client_test.go, client/servicediscovery/resource_manager_service_discovery_test.go
Updates mocks/fakes and test wiring to use chan *metastorage.WatchResponse and adjust injected payloads accordingly.
Watch behavior validation and integration tests
tests/integrations/client/client_test.go, tests/integrations/mcs/resourcemanager/resource_manager_test.go
Adjusts tests to iterate res.Events; adds watchCountingResourceGroupProvider and TestKeyspaceResourceGroupControllerWatchCount to validate watch invocation behavior under compaction/failpoints.

Sequence Diagram

sequenceDiagram
  participant Ctrl as ResourceGroupController
  participant MetaClient as MetaStorage Client
  participant GRPCStream as gRPC Watch Stream
  participant Channel as WatchResponse Channel

  Ctrl->>MetaClient: Watch(ctx, key)
  MetaClient->>GRPCStream: Start watch stream
  GRPCStream-->>MetaClient: resp with Events / header

  alt Stream receives events
    MetaClient->>MetaClient: Build WatchResponse(Events)
    MetaClient->>Channel: Send WatchResponse
    Channel-->>Ctrl: Receive WatchResponse
    Ctrl->>Ctrl: Process resp.Events
  else DATA_COMPACTED detected
    GRPCStream-->>MetaClient: header DATA_COMPACTED
    MetaClient->>MetaClient: Extract CompactRevision from header
    MetaClient->>MetaClient: Build WatchResponse with CompactRevision
    MetaClient->>Channel: Send WatchResponse
    Channel-->>Ctrl: Receive WatchResponse
    Ctrl->>Ctrl: Advance cfgRevision from CompactRevision
  else Stream error
    MetaClient->>MetaClient: Log error and build error WatchResponse
    MetaClient->>Channel: Send error WatchResponse
    MetaClient->>GRPCStream: Close
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

  • tikv/pd#10543: Modifies the same controller watch startup/revision anchoring logic.

Suggested labels

needs-cherry-pick-release-8.5, lgtm

Suggested reviewers

  • lhy1024
  • disksing
  • okJiang

"I am a rabbit tracing streams,
Bundling events in tidy teams,
Compact revisions tucked in line,
Controllers wake as watches align,
Hooray — the watcher’s set to shine! 🐇"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: fixing resource group config watch recovery by propagating compact revision information to enable proper watch retry behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed PR description is comprehensive with issue numbers, clear problem statement, implementation summary, and release notes, but lacks detailed commit message and explicit test descriptions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

@ti-chi-bot ti-chi-bot Bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Jun 11, 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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
tests/integrations/mcs/resourcemanager/resource_manager_test.go (1)

94-98: 💤 Low value

Redundant condition check.

Line 95 checks op.Revision > 0 again, but this is already guaranteed by the outer condition on line 94.

♻️ Simplify the condition
 	if op.Revision > 0 {
-		if p.firstRevision == 0 && op.Revision > 0 {
+		if p.firstRevision == 0 {
 			p.firstRevision = op.Revision
 		} else if op.Revision == p.firstRevision {
 			p.watchTimes++
🤖 Prompt for 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.

In `@tests/integrations/mcs/resourcemanager/resource_manager_test.go` around lines
94 - 98, The inner conditional redundantly re-checks op.Revision > 0; simplify
the block by removing that redundant check and just test p.firstRevision: in the
scope where op.Revision > 0 is already true, replace the inner if (currently
using p.firstRevision == 0 && op.Revision > 0) with if p.firstRevision == 0 to
set p.firstRevision = op.Revision, and keep the else if op.Revision ==
p.firstRevision branch to increment p.watchTimes (references: op.Revision,
p.firstRevision, p.watchTimes).
🤖 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 `@client/meta_storage_client.go`:
- Around line 168-180: The code incorrectly sets compactRevision from
header.GetRevision(); change it to use resp.GetCompactRevision() when
header.Error.Type == meta_storagepb.ErrorType_DATA_COMPACTED so the client
retries from the server-provided retry position (update the conditional that
reads resp.GetHeader() and header.Error to assign compactRevision =
resp.GetCompactRevision()); also correct the comment "If mets" to "If meets" and
ensure the metaStorageResp still populates CompactRevision from the corrected
variable.

In `@client/resource_group/controller/global_controller_test.go`:
- Around line 105-107: The mock Watch currently casts to chan
*metastorage.WatchResponse but other stubs still create/send chan
[]*meta_storagepb.Event, causing interface conversion panics; update all mock
stubs (those that currently return chan []*meta_storagepb.Event) and any test
sender paths to return/send chan *metastorage.WatchResponse instead, and when
sending wrap the event slice inside a &metastorage.WatchResponse{Events: events}
(so callers read resp.Events), keeping the MockResourceGroupProvider.Watch
signature and returned channel type consistent across the file.

In `@pkg/mcs/metastorage/server/grpc_service.go`:
- Around line 101-106: The Compact call in the failpoint path currently ignores
its returned error; capture the error from s.manager.client.Compact(s.ctx,
int64(rev)) and handle it instead of discarding it — e.g., if err != nil {
s.logger.Errorf("compact failed during failpoint: %v", err); /* or
return/propagate error as appropriate for the surrounding function */ } so
failures are visible during tests; update the failpoint.Inject block to check
the error and either log or propagate it.

---

Nitpick comments:
In `@tests/integrations/mcs/resourcemanager/resource_manager_test.go`:
- Around line 94-98: The inner conditional redundantly re-checks op.Revision >
0; simplify the block by removing that redundant check and just test
p.firstRevision: in the scope where op.Revision > 0 is already true, replace the
inner if (currently using p.firstRevision == 0 && op.Revision > 0) with if
p.firstRevision == 0 to set p.firstRevision = op.Revision, and keep the else if
op.Revision == p.firstRevision branch to increment p.watchTimes (references:
op.Revision, p.firstRevision, p.watchTimes).
🪄 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

Run ID: 27d428ad-10b7-4a0a-812c-2a2fd1050e45

📥 Commits

Reviewing files that changed from the base of the PR and between 27770cd and 5fbfd03.

📒 Files selected for processing (10)
  • client/clients/metastorage/client.go
  • client/meta_storage_client.go
  • client/resource_group/controller/global_controller.go
  • client/resource_group/controller/global_controller_test.go
  • client/resource_manager_client.go
  • client/resource_manager_client_test.go
  • client/servicediscovery/resource_manager_service_discovery_test.go
  • pkg/mcs/metastorage/server/grpc_service.go
  • tests/integrations/client/client_test.go
  • tests/integrations/mcs/resourcemanager/resource_manager_test.go

Comment thread client/meta_storage_client.go
Comment thread client/resource_group/controller/global_controller_test.go
Comment thread pkg/mcs/metastorage/server/grpc_service.go Outdated
Comment thread client/clients/metastorage/client.go
Signed-off-by: bufferflies <1045931706@qq.com>
@bufferflies

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.29730% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 79.19%. Comparing base (6e3d72f) to head (37fd486).
⚠️ Report is 6 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #10883      +/-   ##
==========================================
+ Coverage   79.14%   79.19%   +0.04%     
==========================================
  Files         536      540       +4     
  Lines       74033    74434     +401     
==========================================
+ Hits        58594    58948     +354     
- Misses      11311    11329      +18     
- Partials     4128     4157      +29     
Flag Coverage Δ
unittests 79.19% <97.29%> (+0.04%) ⬆️

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.

@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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/integrations/client/client_test.go (1)

1556-1564: ⚠️ Potential issue | 🟡 Minor

Guard watch channel receive to avoid nil deref (res.Events)

client.Watch returns chan *metastorage.WatchResponse; when the watch goroutine closes ch, a receive can yield res == nil, and res.Events will panic. Add an ok check before iterating.

Suggested fix
-		case res := <-ch:
-			for _, r := range res.Events {
+		case res, ok := <-ch:
+			if !ok {
+				re.FailNow("watch channel closed unexpectedly")
+			}
+			for _, r := range res.Events {
 				watchCount++
 				if r.GetType() == meta_storagepb.Event_DELETE {
 					re.Equal(watchPrefix+string(r.PrevKv.Value), string(r.Kv.Key))
 				} else {
 					re.Equal(watchPrefix+string(r.Kv.Value), string(r.Kv.Key))
 				}
 			}
🤖 Prompt for 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.

In `@tests/integrations/client/client_test.go` around lines 1556 - 1564, The test
reads from the watch channel without guarding for a closed channel which can
yield res == nil and cause a panic when accessing res.Events; change the receive
to "res, ok := <-ch" (or check res == nil) and skip or break when !ok || res ==
nil before iterating over res.Events, so update the block that uses client.Watch
/ ch / WatchResponse / res.Events to handle a closed channel safely.
🤖 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/mcs/metastorage/server/grpc_service.go`:
- Around line 121-123: The Watch handler is reading from watchChan with "res :=
<-watchChan" without checking whether the channel was closed, which can cause a
tight loop on a nil/zero WatchResponse; modify the receive to use the two-value
form (res, ok := <-watchChan) in the Watch method and immediately handle !ok by
breaking/returning (or cancelling the watch and cleaning up) so the loop exits
when etcd closes the channel; ensure subsequent code that uses res (res.Err(),
res.Events) only runs when ok is true.

---

Outside diff comments:
In `@tests/integrations/client/client_test.go`:
- Around line 1556-1564: The test reads from the watch channel without guarding
for a closed channel which can yield res == nil and cause a panic when accessing
res.Events; change the receive to "res, ok := <-ch" (or check res == nil) and
skip or break when !ok || res == nil before iterating over res.Events, so update
the block that uses client.Watch / ch / WatchResponse / res.Events to handle a
closed channel safely.
🪄 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

Run ID: 46eb51ce-b1a3-4847-a9bf-e45581e6444f

📥 Commits

Reviewing files that changed from the base of the PR and between 27770cd and c80cf92.

📒 Files selected for processing (10)
  • client/clients/metastorage/client.go
  • client/meta_storage_client.go
  • client/resource_group/controller/global_controller.go
  • client/resource_group/controller/global_controller_test.go
  • client/resource_manager_client.go
  • client/resource_manager_client_test.go
  • client/servicediscovery/resource_manager_service_discovery_test.go
  • pkg/mcs/metastorage/server/grpc_service.go
  • tests/integrations/client/client_test.go
  • tests/integrations/mcs/resourcemanager/resource_manager_test.go

Comment thread pkg/mcs/metastorage/server/grpc_service.go Outdated
@ti-chi-bot ti-chi-bot Bot added needs-1-more-lgtm Indicates a PR needs 1 more LGTM. approved labels Jun 11, 2026
bufferflies and others added 4 commits June 11, 2026 17:57
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: tongjian <1045931706@qq.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: tongjian <1045931706@qq.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: tongjian <1045931706@qq.com>
@ti-chi-bot

ti-chi-bot Bot commented Jun 12, 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 added lgtm and removed needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels Jun 12, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

[LGTM Timeline notifier]

Timeline:

  • 2026-06-11 09:26:09.321629141 +0000 UTC m=+1038470.391946541: ☑️ agreed by rleungx.
  • 2026-06-12 08:11:01.844871574 +0000 UTC m=+1120362.915188964: ☑️ agreed by okJiang.

@bufferflies

Copy link
Copy Markdown
Contributor Author

/retest

@ti-chi-bot ti-chi-bot Bot merged commit 8ed2b3f into tikv:master Jun 12, 2026
41 of 43 checks passed
@ti-chi-bot

ti-chi-bot Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

@bufferflies: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
pull-unit-test-next-gen-3 37fd486 link unknown /test pull-unit-test-next-gen-3

Full PR test history. Your PR dashboard.

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. I understand the commands that are listed here.

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

Labels

approved dco-signoff: yes Indicates the PR's author has signed the dco. lgtm release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

client: fix resource group config watch recovery "watch request" log flood

3 participants