Skip to content

server, member: check target PD readiness before leader transfer#10994

Open
HunDunDM wants to merge 3 commits into
tikv:masterfrom
HunDunDM:hundundm/primary_region_loaded
Open

server, member: check target PD readiness before leader transfer#10994
HunDunDM wants to merge 3 commits into
tikv:masterfrom
HunDunDM:hundundm/primary_region_loaded

Conversation

@HunDunDM

@HunDunDM HunDunDM commented Jul 10, 2026

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: Close #10993

PD exposes /pd/api/v2/ready to indicate whether a member has finished initial region loading and can be promoted to leader. Before this change, selected PD leader transfer paths could move embedded etcd leadership to a target PD that had not finished region loading.

What is changed and how does it work?

This PR adds an opt-in target readiness check before selected embedded etcd leader transfers.

Key changes:

  • Add ReadyAPI feature metadata in pkg/versioninfo.
  • Add optional target checker support for MoveEtcdLeader, ResignEtcdLeader, and CheckPriority.
  • Enable the checker for:
    • /pd/api/v1/leader/transfer/{next_leader}
    • /pd/api/v1/leader/resign
    • leader-priority based automatic transfer
  • Keep recovery paths and frequent campaign protection paths unchanged.
  • Avoid duplicate readiness checks after ResignEtcdLeader filters ready candidates.
  • Add focused tests for version gating, ready checks, manual transfer, resign candidate filtering, and priority transfer behavior.

Check List

Tests

  • Unit test
  • Integration test

Code changes

  • Has HTTP APIs changed

Release note

PD now checks target PD readiness before selected leader transfers to avoid transferring leadership to members that have not finished initial region loading.

Summary by CodeRabbit

  • New Features

    • Added leader-transfer readiness checks to avoid transferring/resigning to unready targets.
    • Introduced Ready API support detection and exposed the API v2 base path for routing.
  • Bug Fixes

    • Transfers/resigns now skip unready candidates/targets and return clear errors when no eligible candidate is available.
    • Automatic leader management logs and skips when target readiness checks fail.
  • Tests

    • Added unit and integration coverage for readiness checks, version support behavior, and leader-transfer/resign scenarios.

HunDunDM added 2 commits July 10, 2026 12:46
Signed-off-by: HunDunDM <hundundm@gmail.com>
Signed-off-by: HunDunDM <hundundm@gmail.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. dco-signoff: yes Indicates the PR's author has signed the dco. labels Jul 10, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign connor1996 for approval. For more information see the Code Review Process.
Please ensure that each of them provides their approval before proceeding.

The full list of commands accepted by this bot can be found 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

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 84bede68-ef8e-47f3-8c23-4e1c4ade9d4e

📥 Commits

Reviewing files that changed from the base of the PR and between a25d4e9 and 65c3040.

📒 Files selected for processing (3)
  • server/member_ready.go
  • server/member_ready_test.go
  • tools/pd-ctl/tests/member/member_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/member_ready.go

📝 Walkthrough

Walkthrough

Leader transfer now supports optional target readiness checks. Manual transfer, resign, and priority-based transfer paths use PD version and readiness endpoints to reject or skip unready members, with new error definitions, API path constants, server checks, and coverage tests.

Changes

Leader transfer readiness

Layer / File(s) Summary
Readiness and API contracts
errors.toml, pkg/errs/errno.go, pkg/utils/apiutil/apiutil.go, pkg/versioninfo/*, server/apiv2/*
Adds the target-check error, v2 API path constant, and ReadyAPI version support detection.
Target readiness probing
server/member_ready.go, server/member_ready_test.go
Resolves member client URLs, checks /version and supported /ready endpoints, and validates HTTP responses with timeout handling.
Transfer option and integration wiring
pkg/member/member.go, server/api/member.go, server/server.go
Adds target-checker options, gates priority transfers, filters resign candidates, and wires manual and automatic paths to readiness checks.
Leader transfer behavior tests
tests/server/member/member_test.go, tests/server/api/member_test.go, tests/integrations/client/http_client_test.go, tools/pd-ctl/tests/member/member_test.go
Covers unready targets, candidate filtering, typed errors, successful transfers, supported ReadyAPI versions, and asynchronous leader changes.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • tikv/pd#10042: Overlaps with the v2 affinity redirect path and router wiring updated here.

Suggested labels: lgtm, approved

Suggested reviewers: rleungx, okJiang, ystaticy, lhy1024

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.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 clearly names the main change: checking target PD readiness before leader transfer.
Description check ✅ Passed The description includes the issue number, problem, changes, checklist, and release note, matching the repository template.
Linked Issues check ✅ Passed The changes implement the required readiness check for manual transfer, resign, and priority transfer while preserving recovery paths.
Out of Scope Changes check ✅ Passed The extra edits are supporting tests and path refactors for the same readiness-transfer feature, not unrelated scope.
✨ 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 added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Jul 10, 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)
pkg/member/member.go (1)

417-421: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Use a timeout-bounded context for the resign candidate checker loop.

MoveEtcdLeader (line 351) correctly uses moveCtx with moveLeaderTimeout for the checker, but ResignEtcdLeader (line 421) passes the raw ctx to options.targetChecker. When called from API handlers via h.svr.Context() (a long-lived context), the checker loop across multiple candidates has no context-level deadline, relying solely on the HTTP client timeout. Adding a timeout-bounded context here would bound the total check duration and align with the MoveEtcdLeader pattern.

♻️ Suggested fix
 	options := newMoveEtcdLeaderOptions(opts...)
 	if options.targetChecker != nil {
+		checkCtx, cancel := context.WithTimeout(ctx, moveLeaderTimeout)
+		defer cancel()
 		readyEtcdLeaderIDs := make([]uint64, 0, len(etcdLeaderIDs))
 		var lastErr error
 		for _, id := range etcdLeaderIDs {
-			if err := options.targetChecker(ctx, id); err != nil {
+			if err := options.targetChecker(checkCtx, id); err != nil {
 				lastErr = err
 				log.Warn("skip unready pd when resigning etcd leader", zap.Uint64("target-member-id", id), errs.ZapError(err))
 				continue
🤖 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 `@pkg/member/member.go` around lines 417 - 421, ResignEtcdLeader’s
target-checker loop uses the unbounded ctx, allowing candidate checks to exceed
the intended operation timeout. Create a timeout-bounded moveCtx using
moveLeaderTimeout before iterating candidates, pass moveCtx to
options.targetChecker, and ensure the context is canceled appropriately,
matching MoveEtcdLeader’s pattern.
🤖 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.

Nitpick comments:
In `@pkg/member/member.go`:
- Around line 417-421: ResignEtcdLeader’s target-checker loop uses the unbounded
ctx, allowing candidate checks to exceed the intended operation timeout. Create
a timeout-bounded moveCtx using moveLeaderTimeout before iterating candidates,
pass moveCtx to options.targetChecker, and ensure the context is canceled
appropriately, matching MoveEtcdLeader’s pattern.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9e769ac2-1240-45f9-86db-dbdcad899e31

📥 Commits

Reviewing files that changed from the base of the PR and between 2b3abf1 and a25d4e9.

📒 Files selected for processing (15)
  • errors.toml
  • pkg/errs/errno.go
  • pkg/member/member.go
  • pkg/utils/apiutil/apiutil.go
  • pkg/versioninfo/feature.go
  • pkg/versioninfo/versioninfo_test.go
  • server/api/member.go
  • server/apiv2/middlewares/microservice_redirector.go
  • server/apiv2/router.go
  • server/member_ready.go
  • server/member_ready_test.go
  • server/server.go
  • tests/integrations/client/http_client_test.go
  • tests/server/api/member_test.go
  • tests/server/member/member_test.go

Signed-off-by: HunDunDM <hundundm@gmail.com>
@HunDunDM

Copy link
Copy Markdown
Member Author

/test pull-unit-test-next-gen-3

@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.61290% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.27%. Comparing base (c2a47d8) to head (65c3040).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #10994      +/-   ##
==========================================
+ Coverage   79.22%   79.27%   +0.04%     
==========================================
  Files         541      542       +1     
  Lines       75965    76091     +126     
==========================================
+ Hits        60187    60320     +133     
- Misses      11531    11537       +6     
+ Partials     4247     4234      -13     
Flag Coverage Δ
unittests 79.27% <76.61%> (+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.

Comment thread pkg/member/member.go
readyEtcdLeaderIDs := make([]uint64, 0, len(etcdLeaderIDs))
var lastErr error
for _, id := range etcdLeaderIDs {
if err := options.targetChecker(ctx, id); err != nil {

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 suggest not passing the outer ctx directly to the target checker here. In the API path, ResignEtcdLeader receives a server-level context with a long lifetime. If the checker blocks, or if a future checker implementation no longer relies only on HTTP request timeouts, the candidate filtering phase would not have a clear time bound.

MoveEtcdLeader already wraps the operation with moveLeaderTimeout; this path should have a similar timeout-bounded context for the checker. One detail: for resign with multiple candidates, a single shared 5s timeout may be consumed by the first unready candidate and prevent checking later ready candidates, so a per-candidate timeout or an explicitly documented total timeout would be safer.

Comment thread server/member_ready.go
if err != nil {
return errors.Annotatef(err, "failed to get target pd member %d version from %s", memberID, clientURL)
}
pdVersion, err := versioninfo.ParseVersion(version)

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.

Could we clarify the compatibility policy for unparsable target versions here? The current logic treats a successful /pd/api/v1/version response with an unparsable version string, such as "None", as if the target supports /pd/api/v2/ready, and then calls the ready API.

This is useful for local/dev builds where PDReleaseVersion can be "None". However, the issue states that if the target version does not support /ready, a successful version request should be treated as ready for compatibility. If an older or custom-built PD returns "None" or another non-standard version string and does not expose /pd/api/v2/ready, the current logic would reject the transfer.

Please make this policy explicit: either document that unparsable/None means a dev/current build and should still check ready, or treat unknown versions as not known to support the ready API and allow the transfer for compatibility. The table case in TestCheckMemberReadyURL should reflect that decision.

Comment thread server/member_ready.go
if err != nil {
return nil, err
}
if clientURLs := findMemberClientURLs(members, memberID); len(clientURLs) > 0 {

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.

This uses cached client URLs as long as the member is found, and only calls ReloadMembers() when the member is missing. If a member’s client URL was just updated, the cache may still contain the old URL; the ready check would fail against the stale address and would not force a reload/retry.

The cache TTL is only 1s, so the impact window is small. Still, leader transfer is a low-frequency admin operation, and one forced reload after all cached URLs fail should be cheap. Consider reloading members and retrying if the cached URLs all fail and the target member’s URLs changed.

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

Labels

dco-signoff: yes Indicates the PR's author has signed the dco. release-note Denotes a PR that will be considered when it comes time to generate release notes. 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.

server: check target PD readiness before leader transfer

2 participants