Skip to content

tools: pdctl support query region direct#10273

Open
bufferflies wants to merge 2 commits intotikv:masterfrom
bufferflies:pdctl/region-direct
Open

tools: pdctl support query region direct#10273
bufferflies wants to merge 2 commits intotikv:masterfrom
bufferflies:pdctl/region-direct

Conversation

@bufferflies
Copy link
Contributor

@bufferflies bufferflies commented Mar 2, 2026

What problem does this PR solve?

Issue Number: Close #10327

What is changed and how does it work?

add two header to allow client query region info.

for _, endpoint := range getEndpoints(cmd) {
	header.Add(apiutil.XForwardedForHeader, endpoint)
}
header.Set(apiutil.PDAllowFollowerHandleHeader, "true")

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
  • No code

Code changes

Side effects

  • Possible performance regression
  • Increased code complexity
  • Breaking backward compatibility

Related changes

Release note

None.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added --direct flag to the region command in pd-ctl, enabling X-Forwarded-For header support for direct PD control requests.
  • Tests

    • Added comprehensive tests to verify direct header behavior and region query handling across cluster members.

Signed-off-by: tongjian <1045931706@qq.com>
@ti-chi-bot
Copy link
Contributor

ti-chi-bot bot commented Mar 2, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@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. release-note-none Denotes a PR that doesn't merit a release note. dco-signoff: yes Indicates the PR's author has signed the dco. labels Mar 2, 2026
@ti-chi-bot
Copy link
Contributor

ti-chi-bot bot commented Mar 2, 2026

[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 hundundm 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
Copy link

coderabbitai bot commented Mar 2, 2026

📝 Walkthrough

Walkthrough

Introduces a "direct" flag for pd-ctl region commands to enable forwarding requests to PD followers with X-Forwarded-For headers, alongside failpoint injection for testing region query rejection on leaders.

Changes

Cohort / File(s) Summary
API Failpoint Injection
server/api/region.go
Injects a RejectGetRegionByIDWhenAccessLeader failpoint in GetRegionByID that sets regionInfo to nil when serving, causing 404 responses for testing leader rejection scenarios.
PD-CTL Direct Header Support
tools/pd-ctl/pdctl/command/global.go, tools/pd-ctl/pdctl/command/region_command.go
Introduces buildDirectHeader() helper to construct HTTP headers with X-Forwarded-For and PDAllowFollowerHandleHeader when the "direct" flag is set. Adds persistent "direct" boolean flag to region command.
Tests for Direct Header and Follower Behavior
tools/pd-ctl/tests/global_test.go, tools/pd-ctl/tests/region/region_test.go
Adds TestRegionDirectHeader() to verify X-Forwarded-For behavior with/without --direct flag and TestFollowerDirect() to validate region queries from followers, including failpoint-based leader rejection assertions.

Sequence Diagram

sequenceDiagram
    participant Client as pd-ctl Client
    participant PD as PD Follower
    participant Leader as PD Leader

    alt Without --direct flag
        Client->>PD: Region request (no direct flag)
        PD->>Leader: Forward to leader
        Note over Leader: RejectGetRegionByIDWhenAccessLeader<br/>failpoint enabled → returns 404
        Leader-->>PD: 404 Not Found
        PD-->>Client: 404 Not Found
    else With --direct flag
        Client->>Client: Build header with X-Forwarded-For<br/>& PDAllowFollowerHandleHeader
        Client->>PD: Region request (direct header set)
        Note over PD: PDAllowFollowerHandleHeader=true<br/>processes locally
        PD-->>Client: Region info
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • okJiang
  • lhy1024
  • rleungx

Poem

🐰 A rabbit hops through regions bright,
With "direct" flags set just right,
Followers now handle their own way,
No need for leaders every day! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is incomplete. While it has the required template structure with an issue reference (Close #10327), the 'What is changed and how does it work?' section lacks substantive detail, and no items in the 'Check List' are explicitly marked as completed despite code changes and tests being present. Complete the description by: (1) Adding detailed explanation of changes in the 'What is changed and how does it work?' section beyond the code snippet; (2) Explicitly marking completed checklist items (e.g., 'Integration test' and 'Has HTTP APIs changed'); (3) Adding a meaningful release note if applicable, not just 'None'.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'tools: pdctl support query region direct' clearly summarizes the main change—adding direct query region support to pdctl, which aligns with the code changes across multiple files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 Mar 2, 2026
Signed-off-by: tongjian <1045931706@qq.com>
@bufferflies bufferflies marked this pull request as ready for review March 10, 2026 08:30
@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 Mar 10, 2026
@bufferflies
Copy link
Contributor Author

/retest

@codecov
Copy link

codecov bot commented Mar 10, 2026

Codecov Report

❌ Patch coverage is 88.23529% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.84%. Comparing base (5885cec) to head (2c50ebd).
⚠️ Report is 20 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #10273      +/-   ##
==========================================
+ Coverage   78.80%   78.84%   +0.03%     
==========================================
  Files         523      527       +4     
  Lines       70529    70937     +408     
==========================================
+ Hits        55580    55929     +349     
- Misses      10955    11000      +45     
- Partials     3994     4008      +14     
Flag Coverage Δ
unittests 78.84% <88.23%> (+0.03%) ⬆️

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
tools/pd-ctl/tests/region/region_test.go (1)

561-564: Consider strengthening the follower assertion.

The assertion accepts either region data ("id":100) OR "TiKV cluster not bootstrapped". This fallback suggests potential test flakiness - if followers are properly handling the request, they should consistently return region data since the cluster was bootstrapped and the region heartbeat was processed.

If the "not bootstrapped" case is a known race condition, consider adding a brief wait or retry logic to ensure cluster state propagation before querying followers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/pd-ctl/tests/region/region_test.go` around lines 561 - 564, The current
test reads output from tests.ExecuteCommand(cmd, "-u", serverAddr, "region",
"100", "--direct") and accepts either "\"id\":100" or "TiKV cluster not
bootstrapped", which masks flakiness; modify the test to retry the
ExecuteCommand call (or add a short wait loop) until the response contains
"\"id\":100" or a timeout is reached, so followers are given time to observe the
bootstrapped/heartbeat state before asserting, and remove the permissive OR
branch once retries/timeouts are exhausted; reference tests.ExecuteCommand and
the region query parameters ("region", "100", "--direct") when implementing the
wait/retry.
tools/pd-ctl/tests/global_test.go (1)

130-142: Confusing test behavior: --direct=false still adds headers.

The comment states the direct flag "does not control whether to add X-Forwarded-For header," but this seems counterintuitive. The test expects --direct=false to still add headers because buildDirectHeader checks Changed("direct") rather than the flag's actual value.

If this is intentional behavior, consider clarifying the flag's purpose in its help text. If --direct=false should disable header addition, the condition in buildDirectHeader should check the flag's boolean value, not just whether it was changed.

♻️ If the intent is that --direct=false should NOT add headers

In global.go:

-	if flag := cmd.Flags().Lookup("direct"); flag != nil && cmd.Flags().Changed("direct") {
+	if direct, _ := cmd.Flags().GetBool("direct"); direct {

And update this test to expect empty headers for --direct=false.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/pd-ctl/tests/global_test.go` around lines 130 - 142, The test observes
that --direct=false still causes X-Forwarded-For to be added because
buildDirectHeader only checks whether the flag was changed (Changed("direct"))
rather than its boolean value; change buildDirectHeader to read the actual
boolean value of the "direct" flag (e.g., via the command's FlagSet/GetBool or
the flags API used in your CLI) and only add the X-Forwarded-For header when
that boolean is true, and update the test (the ExecuteCommand call with
"--direct=false") to expect no forwarded header when the flag is false.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tools/pd-ctl/pdctl/command/global.go`:
- Around line 193-205: The buildDirectHeader function uses
cmd.Flags().Lookup("direct") which only sees local flags; change it to use
cmd.Flag("direct") so inherited persistent flags are detected (check the
returned *pflag.Flag for nil and use its .Changed property). Update the
conditional that currently references flag := cmd.Flags().Lookup("direct") to
obtain the flag via cmd.Flag("direct") and keep the rest of the logic (iterating
getEndpoints(cmd), adding apiutil.XForwardedForHeader, and setting
apiutil.PDAllowFollowerHandleHeader) unchanged.

---

Nitpick comments:
In `@tools/pd-ctl/tests/global_test.go`:
- Around line 130-142: The test observes that --direct=false still causes
X-Forwarded-For to be added because buildDirectHeader only checks whether the
flag was changed (Changed("direct")) rather than its boolean value; change
buildDirectHeader to read the actual boolean value of the "direct" flag (e.g.,
via the command's FlagSet/GetBool or the flags API used in your CLI) and only
add the X-Forwarded-For header when that boolean is true, and update the test
(the ExecuteCommand call with "--direct=false") to expect no forwarded header
when the flag is false.

In `@tools/pd-ctl/tests/region/region_test.go`:
- Around line 561-564: The current test reads output from
tests.ExecuteCommand(cmd, "-u", serverAddr, "region", "100", "--direct") and
accepts either "\"id\":100" or "TiKV cluster not bootstrapped", which masks
flakiness; modify the test to retry the ExecuteCommand call (or add a short wait
loop) until the response contains "\"id\":100" or a timeout is reached, so
followers are given time to observe the bootstrapped/heartbeat state before
asserting, and remove the permissive OR branch once retries/timeouts are
exhausted; reference tests.ExecuteCommand and the region query parameters
("region", "100", "--direct") when implementing the wait/retry.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 13618ab6-2a5d-4acc-bc09-ff35235fc6b2

📥 Commits

Reviewing files that changed from the base of the PR and between 294ed96 and 2c50ebd.

📒 Files selected for processing (5)
  • server/api/region.go
  • tools/pd-ctl/pdctl/command/global.go
  • tools/pd-ctl/pdctl/command/region_command.go
  • tools/pd-ctl/tests/global_test.go
  • tools/pd-ctl/tests/region/region_test.go

Comment on lines +193 to +205
func buildDirectHeader(cmd *cobra.Command, customHeader http.Header) http.Header {
header := customHeader.Clone()
if header == nil {
header = http.Header{}
}
if flag := cmd.Flags().Lookup("direct"); flag != nil && cmd.Flags().Changed("direct") {
for _, endpoint := range getEndpoints(cmd) {
header.Add(apiutil.XForwardedForHeader, endpoint)
}
header.Set(apiutil.PDAllowFollowerHandleHeader, "true")
}
return header
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use cmd.Flag() instead of cmd.Flags().Lookup() to find inherited persistent flags.

cmd.Flags().Lookup("direct") only searches local flags on the current command. Since --direct is defined as a PersistentFlag on the region command, subcommands won't find it with this lookup. Use cmd.Flag("direct") which searches both local and inherited flags.

🐛 Proposed fix
 func buildDirectHeader(cmd *cobra.Command, customHeader http.Header) http.Header {
 	header := customHeader.Clone()
 	if header == nil {
 		header = http.Header{}
 	}
-	if flag := cmd.Flags().Lookup("direct"); flag != nil && cmd.Flags().Changed("direct") {
+	if flag := cmd.Flag("direct"); flag != nil && flag.Changed {
 		for _, endpoint := range getEndpoints(cmd) {
 			header.Add(apiutil.XForwardedForHeader, endpoint)
 		}
 		header.Set(apiutil.PDAllowFollowerHandleHeader, "true")
 	}
 	return header
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/pd-ctl/pdctl/command/global.go` around lines 193 - 205, The
buildDirectHeader function uses cmd.Flags().Lookup("direct") which only sees
local flags; change it to use cmd.Flag("direct") so inherited persistent flags
are detected (check the returned *pflag.Flag for nil and use its .Changed
property). Update the conditional that currently references flag :=
cmd.Flags().Lookup("direct") to obtain the flag via cmd.Flag("direct") and keep
the rest of the logic (iterating getEndpoints(cmd), adding
apiutil.XForwardedForHeader, and setting apiutil.PDAllowFollowerHandleHeader)
unchanged.

@ti-chi-bot
Copy link
Contributor

ti-chi-bot bot commented Mar 10, 2026

@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 2c50ebd link true /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.

if header == nil {
header = http.Header{}
}
if flag := cmd.Flags().Lookup("direct"); flag != nil && cmd.Flags().Changed("direct") {
Copy link
Contributor

@lhy1024 lhy1024 Mar 10, 2026

Choose a reason for hiding this comment

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

cmd.Flags().Changed("direct") is also true for --direct=false, so this will still enable the direct path when the flag is explicitly set to false.

Comment on lines +199 to +201
for _, endpoint := range getEndpoints(cmd) {
header.Add(apiutil.XForwardedForHeader, endpoint)
}
Copy link
Contributor

@lhy1024 lhy1024 Mar 10, 2026

Choose a reason for hiding this comment

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

We should remove it, PD-Allow-follower-handle is enough.

We should not set X-Forwarded-For to PD endpoints. PD treats X-Forwarded-For as the caller/source IP, so adding PD addresses here changes the request metadata unexpectedly.

For example, with -u pd1:2379,pd2:2379,pd3:2379, the request logic may still try all endpoints, but the server-side parsing of X-Forwarded-For will effectively see the first value and record pd1:2379 as the source, which is incorrect.

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-none Denotes a PR that doesn't merit a release note. 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.

pd-ctl can query region infos of the follower nodes

2 participants