Skip to content

meta-service-group: add status#10904

Merged
ti-chi-bot[bot] merged 8 commits into
tikv:masterfrom
bufferflies:pr-cp-10889-meta-service-group-status
Jul 3, 2026
Merged

meta-service-group: add status#10904
ti-chi-bot[bot] merged 8 commits into
tikv:masterfrom
bufferflies:pr-cp-10889-meta-service-group-status

Conversation

@bufferflies

@bufferflies bufferflies commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: Close #10889

CP from 5be7eae (original PR #392).

Original author: @AmoebaProtozoa.

What is changed and how does it work?

meta-service-group: add status

Add persisted meta-service group status with assignment count and enabled state. Only enabled meta-service groups are eligible for keyspace assignment, the HTTP v2 API returns the full status, and pd-ctl can patch enabled state or assignment count through the status endpoint.

Check List

Tests

  • Unit test

Code changes

Related changes

  • Need to cherry-pick to the release branch

Validation:

  • GOFLAGS=-buildvcs=false go test ./pkg/keyspace ./pkg/storage ./server/apiv2/handlers -run 'TestMetaServiceGroup|TestNonExistent' -count=1 -timeout=5m
  • GOFLAGS=-buildvcs=false go test ./tests/server/apiv2/handlers -run TestMetaServiceGroupTestSuite -count=1 -timeout=5m
  • cd tools/pd-ctl && GOFLAGS=-buildvcs=false go test ./pdctl/command -run TestNonExistent -count=0 -timeout=5m
  • git diff --check
  • GOFLAGS=-buildvcs=false make check

Release note

None.

Summary by CodeRabbit

  • New Features

    • Added a new API to patch meta-service group status, including enabled and assignment_count, plus a companion CLI to set enabled state and assignment count.
    • API responses now expose detailed group status (assignment count + enabled) alongside assigned keyspaces.
  • Bug Fixes

    • Assignment tracking now uses persisted group status (not separate counters), including correct handling after group deletion and fallback counts.
    • Negative assignment counts are rejected; reassignment blocks disabled destination groups.
    • Keyspace config errors now return clear 400 responses for unknown/disabled groups, and keyspaces can be created when no groups are enabled.

@ti-chi-bot ti-chi-bot Bot added do-not-merge/needs-triage-completed 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 Jun 16, 2026
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

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

The PR replaces per-group integer assignment-count persistence with JSON-persisted MetaServiceGroupStatus records containing assignment_count and enabled. Manager logic, HTTP handlers, CLI commands, and tests now read and write status records, and a new status PATCH endpoint is added.

Changes

Meta-service group status migration

Layer / File(s) Summary
Storage contract and status key path
pkg/utils/keypath/absolute_key_path.go, pkg/storage/endpoint/meta_service_group.go, pkg/storage/meta_service_group_test.go
Replaces assignment-count keys and methods with status keys and status load/save/remove methods; adds MetaServiceGroupStatus; rewrites storage tests to persist and compare full status records.
Manager reads, selection, and errors
pkg/keyspace/util.go, pkg/keyspace/meta_service_group.go, pkg/keyspace/meta_service_group_test.go
Exports the unknown-group and invalid-count errors; adds GetStatus, derives counts from loaded statuses, selects enabled groups from status data, and updates tests for status-based reads.
Manager patching and assignment updates
pkg/keyspace/meta_service_group.go, pkg/keyspace/keyspace.go, pkg/keyspace/keyspace_test.go, pkg/keyspace/meta_service_group_test.go, server/apiv2/handlers/keyspace.go
Applies status patches, updates assignment and reassignment flows to mutate persisted status records, clears deleted-group status, falls back when no enabled group is available, and updates keyspace tests/handler error mapping.
Status API and CLI commands
server/apiv2/handlers/meta_service_group.go, tests/server/apiv2/handlers/*, tools/pd-ctl/pdctl/command/meta_service_group_command.go, tools/pd-ctl/tests/metaservicegroup/meta_service_group_test.go
Adds PATCH /meta-service-groups/:id/status, returns embedded status data in meta-service-group responses, maps conflicts and validation errors, and adds CLI commands plus integration coverage for enable and assignment-count updates.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant PatchMetaServiceGroupStatus
  participant MetaServiceGroupManager
  participant StorageEndpoint

  Client->>PatchMetaServiceGroupStatus: PATCH /meta-service-groups/:id/status
  PatchMetaServiceGroupStatus->>MetaServiceGroupManager: PatchStatus(ctx, id, patch)
  MetaServiceGroupManager->>StorageEndpoint: RunInTxn → LoadMetaServiceGroupStatus
  StorageEndpoint-->>MetaServiceGroupManager: status map
  MetaServiceGroupManager->>StorageEndpoint: SaveMetaServiceGroupStatus
  PatchMetaServiceGroupStatus->>MetaServiceGroupManager: GetStatus(ctx)
  MetaServiceGroupManager->>StorageEndpoint: LoadMetaServiceGroupStatus
  StorageEndpoint-->>PatchMetaServiceGroupStatus: groups + statuses
  PatchMetaServiceGroupStatus-->>Client: MetaServiceGroupStatus[]
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • tikv/pd#10597: Touches the same meta-service-group storage and manager code paths around assignment-count persistence.

Suggested labels

lgtm, approved

Suggested reviewers

  • lhy1024
  • AmoebaProtozoa
  • rleungx

Poem

🐇 I hop through status, bright and neat,
enabled and counts now share one seat.
The CLI pings, the API sings,
And JSON foxes all the things.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 39.29% 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 It concisely names the main change: adding meta-service-group status.
Description check ✅ Passed It includes the issue number, summary of changes, tests, code changes, and related-changes sections required by the template.
Linked Issues check ✅ Passed The PR adds persisted status, enabled-state gating, API updates, and pd-ctl support as required by #10889.
Out of Scope Changes check ✅ Passed The changes described all align with the status-tracking feature and its supporting tests, API, and CLI updates.
✨ 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/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Jun 16, 2026
@bufferflies

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@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

🤖 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/keyspace/meta_service_group.go`:
- Around line 75-79: The MetaServiceGroupStatusPatch struct uses the JSON field
name assigned_count for the AssignedCount field, but the MetaServiceGroupStatus
exposes this field as assignment_count. This mismatch prevents API clients from
updating the count using the status shape. Change the JSON tag on the
AssignedCount field in MetaServiceGroupStatusPatch to assignment_count to align
with the status struct. Additionally, add validation logic to reject negative
values before persisting the patch, since negative counts corrupt the balancing
logic. Be aware that callers constructing MetaServiceGroupStatusPatch instances
may need updating to use the corrected field name.

In `@pkg/storage/endpoint/meta_service_group.go`:
- Around line 62-70: In the loadMetaServiceGroupStatus function, when statusVal
is empty (indicating the /status key is missing), add a fallback mechanism
before returning the zero-value status. Before the return statement in the
statusVal == "" condition, attempt to load the legacy assignment-count key using
the transaction (txn). If the legacy key exists, migrate its integer value to
populate the assignment_count field of the status object and set enabled to
true. Only return the zero-value status with assignment_count:0 and
enabled:false if the legacy key is also not found, ensuring existing persisted
counts from the old storage model are preserved during the upgrade.

In `@server/apiv2/handlers/meta_service_group.go`:
- Around line 187-195: The PatchMetaServiceGroupStatus handler needs to validate
the patch payload before persisting it and properly classify errors. After the
ShouldBindJSON check succeeds, add validation logic to ensure the patch data is
valid (such as checking that assigned_count is not negative), and return a 400
Bad Request if validation fails. Additionally, when the manager.PatchStatus call
fails, distinguish between client errors (invalid patch data) and server errors
instead of treating all failures as 500 Internal Server Error. Return 400 for
validation or client-side errors and 500 only for actual server-side failures.
🪄 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: c4f09485-f513-4fe7-917b-6ff801605390

📥 Commits

Reviewing files that changed from the base of the PR and between 5eb3dab and c5c65b5.

📒 Files selected for processing (9)
  • pkg/keyspace/meta_service_group.go
  • pkg/keyspace/meta_service_group_test.go
  • pkg/storage/endpoint/meta_service_group.go
  • pkg/storage/meta_service_group_test.go
  • pkg/utils/keypath/absolute_key_path.go
  • server/apiv2/handlers/meta_service_group.go
  • tests/server/apiv2/handlers/meta_service_group_test.go
  • tests/server/apiv2/handlers/testutil.go
  • tools/pd-ctl/pdctl/command/meta_service_group_command.go

Comment thread pkg/keyspace/meta_service_group.go Outdated
Comment thread pkg/storage/endpoint/meta_service_group.go
Comment thread server/apiv2/handlers/meta_service_group.go

@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

🧹 Nitpick comments (3)
tests/server/apiv2/handlers/meta_service_group_test.go (1)

78-100: 💤 Low value

Potential logic discrepancy in collectStatus setting Enabled=true.

The collectStatus helper function at line 89 sets Enabled: true for all collected statuses. However, this represents the expected state after keyspaces have been created and assigned to enabled groups. This is correct in the context of the test since groups are enabled at lines 108-112 before keyspaces are created at line 114.

The logic is correct but could be clearer. Consider adding a comment explaining that this helper assumes all collected groups have been enabled before assignment, to avoid confusion.

🤖 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/server/apiv2/handlers/meta_service_group_test.go` around lines 78 -
100, Add a clarifying comment in the collectStatus function above or near the
line where Enabled is set to true (within the initialization of the
handlers.MetaServiceGroupStatus struct) to explicitly document the assumption
that this helper function expects all collected meta service groups to have been
enabled before keyspaces are assigned to them. This will make the intent clearer
to future readers and prevent confusion about why Enabled is always set to true.
tools/pd-ctl/pdctl/command/meta_service_group_command.go (1)

166-172: 💤 Low value

Consider validating non-negative assignment count client-side.

strconv.Atoi accepts negative integers. While the server should also validate this, adding a client-side check provides better UX by catching errors early with a clear message.

Proposed fix
 func newSetMetaServiceGroupAssignmentCountFunc(cmd *cobra.Command, args []string) {
 	groupID := strings.TrimSpace(args[0])
 	assignmentCount, err := strconv.Atoi(args[1])
 	if err != nil {
 		cmd.PrintErrln("Invalid value for assignment count, must be an integer:", err)
 		return
 	}
+	if assignmentCount < 0 {
+		cmd.PrintErrln("Invalid value for assignment count, must be non-negative")
+		return
+	}
 	patch := &keyspace.MetaServiceGroupStatusPatch{
🤖 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 `@tools/pd-ctl/pdctl/command/meta_service_group_command.go` around lines 166 -
172, After successfully parsing the assignment count with strconv.Atoi in the
newSetMetaServiceGroupAssignmentCountFunc function, add a validation check to
ensure assignmentCount is not negative. If assignmentCount is less than zero,
use cmd.PrintErrln to display an error message indicating that the assignment
count must be a non-negative integer, then return early. This provides better
user experience by catching invalid input on the client side before sending it
to the server.
pkg/keyspace/meta_service_group.go (1)

149-161: ⚡ Quick win

Redundant status load within the same transaction.

findMinMetaGroup (called at line 151) already loads the entire status map via LoadMetaServiceGroupStatus. Lines 155-158 load the same data again within the same transaction. Consider refactoring findMinMetaGroup to return both the selected group and the status map, allowing reuse here.

♻️ Suggested refactor
-func (m *MetaServiceGroupManager) findMinMetaGroup(txn kv.Txn) (string, error) {
+func (m *MetaServiceGroupManager) findMinMetaGroup(txn kv.Txn) (string, map[string]*endpoint.MetaServiceGroupStatus, error) {
 	statusMap, err := m.store.LoadMetaServiceGroupStatus(txn, m.metaServiceGroups)
 	if err != nil {
-		return "", err
+		return "", nil, err
 	}
 	minCount := math.MaxInt
 	var assignedGroup string
 	for currentGroup, status := range statusMap {
 		if status.Enabled && status.AssignmentCount < minCount {
 			minCount = status.AssignmentCount
 			assignedGroup = currentGroup
 		}
 	}
 	if assignedGroup == "" {
-		return "", errNoAvailableMetaServiceGroups
+		return "", nil, errNoAvailableMetaServiceGroups
 	}
-	return assignedGroup, nil
+	return assignedGroup, statusMap, nil
 }

Then in AssignToGroup:

-		assignedGroup, err = m.findMinMetaGroup(txn)
+		var statusMap map[string]*endpoint.MetaServiceGroupStatus
+		assignedGroup, statusMap, err = m.findMinMetaGroup(txn)
 		if err != nil {
 			return err
 		}
-		statusMap, err := m.store.LoadMetaServiceGroupStatus(txn, m.metaServiceGroups)
-		if err != nil {
-			return err
-		}
 		status := statusMap[assignedGroup]
🤖 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/keyspace/meta_service_group.go` around lines 149 - 161, The code is
redundantly loading the status map twice within the same transaction: once
inside findMinMetaGroup and again at lines 155-158. Refactor the
findMinMetaGroup method to return both the assigned group and the status map it
already loads internally. Then update the call to findMinMetaGroup in the
transaction to receive both return values, and remove the redundant
m.store.LoadMetaServiceGroupStatus call. Finally, use the status map returned
from findMinMetaGroup instead of loading it again.
🤖 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/keyspace/meta_service_group.go`:
- Around line 173-178: The assignment count decrement for the old group ID in
the statusMap has no validation to prevent it from going negative. Before
decrementing status.AssignmentCount for the oldGroupID in the statusMap, add a
check to ensure the count is greater than 0 to prevent it from becoming
negative, which would corrupt load balancing behavior in findMinMetaGroup. Only
perform the decrement and subsequent SaveMetaServiceGroupStatus call if the
count is positive.

---

Nitpick comments:
In `@pkg/keyspace/meta_service_group.go`:
- Around line 149-161: The code is redundantly loading the status map twice
within the same transaction: once inside findMinMetaGroup and again at lines
155-158. Refactor the findMinMetaGroup method to return both the assigned group
and the status map it already loads internally. Then update the call to
findMinMetaGroup in the transaction to receive both return values, and remove
the redundant m.store.LoadMetaServiceGroupStatus call. Finally, use the status
map returned from findMinMetaGroup instead of loading it again.

In `@tests/server/apiv2/handlers/meta_service_group_test.go`:
- Around line 78-100: Add a clarifying comment in the collectStatus function
above or near the line where Enabled is set to true (within the initialization
of the handlers.MetaServiceGroupStatus struct) to explicitly document the
assumption that this helper function expects all collected meta service groups
to have been enabled before keyspaces are assigned to them. This will make the
intent clearer to future readers and prevent confusion about why Enabled is
always set to true.

In `@tools/pd-ctl/pdctl/command/meta_service_group_command.go`:
- Around line 166-172: After successfully parsing the assignment count with
strconv.Atoi in the newSetMetaServiceGroupAssignmentCountFunc function, add a
validation check to ensure assignmentCount is not negative. If assignmentCount
is less than zero, use cmd.PrintErrln to display an error message indicating
that the assignment count must be a non-negative integer, then return early.
This provides better user experience by catching invalid input on the client
side before sending it to the server.
🪄 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: beec8daa-b422-480f-885e-71d385de0445

📥 Commits

Reviewing files that changed from the base of the PR and between 5eb3dab and c5c65b5.

📒 Files selected for processing (9)
  • pkg/keyspace/meta_service_group.go
  • pkg/keyspace/meta_service_group_test.go
  • pkg/storage/endpoint/meta_service_group.go
  • pkg/storage/meta_service_group_test.go
  • pkg/utils/keypath/absolute_key_path.go
  • server/apiv2/handlers/meta_service_group.go
  • tests/server/apiv2/handlers/meta_service_group_test.go
  • tests/server/apiv2/handlers/testutil.go
  • tools/pd-ctl/pdctl/command/meta_service_group_command.go

Comment thread pkg/keyspace/meta_service_group.go Outdated
AssignedKeyspaces int `json:"assigned_keyspaces"`
ID string `json:"id"`
Addresses string `json:"addresses"`
Status *endpoint.MetaServiceGroupStatus `json:"status,omitempty"`

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 changes the existing v2 response shape by removing assigned_keyspaces. Existing clients of GET/POST /meta-service-groups may break; please keep the old field while adding status, or version the API change.

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.

Good point. Restored the assigned_keyspaces field on the response in 10f7107 — it now mirrors status.assignment_count, so existing GET/POST /meta-service-groups clients keep working, while the new status object additionally exposes the enabled state. Happy to instead version the endpoint if you'd prefer a cleaner break.

@ti-chi-bot ti-chi-bot Bot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed do-not-merge/needs-triage-completed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Jun 25, 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: 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 `@server/apiv2/handlers/meta_service_group.go`:
- Around line 161-169: The status assembly in MetaServiceGroupStatus is
returning a partial object when statuses[id] is missing, which can hide the
enabled state while still emitting AssignedKeyspaces as 0. Update the GetStatus
response handling in meta_service_group.go so it does not append a group unless
a matching endpoint.MetaServiceGroupStatus exists, or else build a full default
status consistently before appending. Keep the behavior aligned with the
MetaServiceGroupStatus struct fields and the statuses[id] lookup.
🪄 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: 5a34b952-41f3-4062-ba82-a462f7dd119a

📥 Commits

Reviewing files that changed from the base of the PR and between b0d53fd and 10f7107.

📒 Files selected for processing (2)
  • pkg/keyspace/meta_service_group.go
  • server/apiv2/handlers/meta_service_group.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/keyspace/meta_service_group.go

Comment thread server/apiv2/handlers/meta_service_group.go Outdated
@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.57360% with 56 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.16%. Comparing base (b36c4da) to head (5fcc61b).
⚠️ Report is 7 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #10904      +/-   ##
==========================================
- Coverage   79.18%   79.16%   -0.03%     
==========================================
  Files         541      541              
  Lines       75312    75603     +291     
==========================================
+ Hits        59638    59849     +211     
- Misses      11445    11519      +74     
- Partials     4229     4235       +6     
Flag Coverage Δ
unittests 79.16% <71.57%> (-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.

c.AbortWithStatusJSON(http.StatusBadRequest, err.Error())
return
}
c.AbortWithStatusJSON(http.StatusInternalServerError, err.Error())

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 map errs.ErrEtcdTxnConflict to 409 here. Concurrent status patches or assignments can hit the etcd txn compare failure, and that is a retryable conflict rather than an internal server error.

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.

Fixed in 6f0571d: errs.ErrEtcdTxnConflict is now mapped to HTTP 409 on both the /status patch and the group patch endpoints, so a lost etcd txn compare surfaces as a retryable conflict rather than 500.

Comment thread pkg/keyspace/keyspace.go Outdated
if manager.mgm != nil && oldMetaServiceGroup != newMetaServiceGroup {
if newMetaServiceGroup != "" && manager.mgm.GetGroups()[newMetaServiceGroup] == "" {
return errUnknownMetaServiceGroup
return ErrUnknownMetaServiceGroup

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 only checks that the target group exists. Since disabled groups are skipped by automatic assignment, config updates should also reject moving a keyspace into a disabled meta-service group.

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.

Fixed in 6f0571d: reassignKeyspaceLocked now loads the target group's status and rejects moving a keyspace into a disabled group with a new ErrMetaServiceGroupDisabled, mapped to HTTP 400 in the keyspace config-update handler.

@bufferflies
bufferflies force-pushed the pr-cp-10889-meta-service-group-status branch from 10f7107 to a9663e8 Compare June 29, 2026 14:31
if currentCount < minCount {
minCount = currentCount
for currentGroup, status := range statusMap {
if status.Enabled && status.AssignmentCount < minCount {

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.

CI is failing because all configured groups start disabled, so keyspace creation gets no available meta-service groups. Please initialize existing groups as enabled or skip assignment when no enabled group exists.

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.

Fixed in 6f0571d: assignGroupAndSaveKeyspace now skips assignment and creates the keyspace without a meta-service group when no enabled group exists (instead of failing with no available meta-service groups), mirroring the empty-group-set fallback. Added a unit test covering the disabled-group case.

@bufferflies
bufferflies requested a review from lhy1024 June 30, 2026 06:57
* add enable status

Signed-off-by: AmoebaProtozoa <8039876+AmoebaProtozoa@users.noreply.github.com>

* Update pkg/keyspace/meta_service_group.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update pkg/keyspace/meta_service_group.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update tests/server/apiv2/handlers/testutil.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix typo

Signed-off-by: AmoebaProtozoa <8039876+AmoebaProtozoa@users.noreply.github.com>

* fix linter issue

Signed-off-by: AmoebaProtozoa <8039876+AmoebaProtozoa@users.noreply.github.com>

* Update tools/pd-ctl/pdctl/command/meta_service_group_command.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix copilot suggestion

Signed-off-by: AmoebaProtozoa <8039876+AmoebaProtozoa@users.noreply.github.com>

* fix ut

Signed-off-by: AmoebaProtozoa <8039876+AmoebaProtozoa@users.noreply.github.com>

---------

Signed-off-by: AmoebaProtozoa <8039876+AmoebaProtozoa@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
(cherry picked from commit 5be7eae)
Signed-off-by: bufferflies <1045931706@qq.com>
@bufferflies
bufferflies force-pushed the pr-cp-10889-meta-service-group-status branch from a9663e8 to 529cb1c Compare June 30, 2026 07:08

@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: 2

🧹 Nitpick comments (2)
pkg/storage/meta_service_group_test.go (1)

68-124: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add a delete round-trip to the storage suite.

This covers save/load and missing-key defaults, but it never verifies that RemoveMetaServiceGroupStatus actually clears persisted state. That is part of the changed contract, and without a direct test a deleted group could silently inherit stale enabled / assignment_count if the same ID is added again.

Proposed test addition
 	checkStatus(re, store, result)
+	re.NoError(store.RunInTxn(context.TODO(), func(txn kv.Txn) error {
+		return store.RemoveMetaServiceGroupStatus(txn, "group3")
+	}))
+	checkStatus(re, store, map[string]*endpoint.MetaServiceGroupStatus{
+		"group3": {},
+	})
 	// should treat extra groups as having empty disabled status.
 	extraGroups := map[string]*endpoint.MetaServiceGroupStatus{
 		"group4": {},
 		"group5": {},
 	}
🤖 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/storage/meta_service_group_test.go` around lines 68 - 124, The storage
test in TestMetaServiceGroupStorage should also cover the delete round-trip for
MetaServiceGroupStatus. After exercising save/load with existing cases, add a
scenario that calls RemoveMetaServiceGroupStatus on a known group via the
storage API, then verify the status is cleared and later re-adding the same
group does not inherit stale AssignmentCount or Enabled values. Use the existing
checkStatus helper and the MetaServiceGroupStatus storage methods to locate the
right behavior.
tools/pd-ctl/tests/metaservicegroup/meta_service_group_test.go (1)

116-142: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert that rejected patch commands leave status unchanged.

These branches only check the error text. If a bad request partially mutated persisted status before failing, this test would still pass. Re-list after each rejected command and confirm etcd-group-0 is still enabled=false with assignment_count=10.

🤖 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 `@tools/pd-ctl/tests/metaservicegroup/meta_service_group_test.go` around lines
116 - 142, The rejected patch-command checks in meta-service-group tests only
verify error messages and do not confirm state was unchanged after failure. In
the test cases around ctl.GetRootCmd() and tests.MustExec for set-enabled and
set-assignment-count, re-list the target group after each rejected command and
assert the persisted status for etcd-group-0 still shows enabled=false and
assignment_count=10. Keep the checks alongside the existing error assertions so
any partial mutation in the patch flow is caught.
🤖 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/keyspace/meta_service_group.go`:
- Around line 190-196: In AssignToGroup, reject negative assignment increments
before updating and saving the MetaServiceGroup status; the current
status.AssignmentCount += count path can persist invalid negative counts and
skew findMinMetaGroup. Add an early validation check in AssignToGroup, before
LoadMetaServiceGroupStatus/SaveMetaServiceGroupStatus, so only non-negative
counts are applied.

In `@server/apiv2/handlers/meta_service_group.go`:
- Around line 173-183: The Swagger annotation for PatchMetaServiceGroupStatus is
missing the path parameter for the {id} segment in the
/meta-service-groups/{id}/status route. Update the comment block on
PatchMetaServiceGroupStatus to declare the id path param alongside the existing
body param, then regenerate the spec with make swagger-spec (SWAGGER=1) so the
generated OpenAPI stays in sync.

---

Nitpick comments:
In `@pkg/storage/meta_service_group_test.go`:
- Around line 68-124: The storage test in TestMetaServiceGroupStorage should
also cover the delete round-trip for MetaServiceGroupStatus. After exercising
save/load with existing cases, add a scenario that calls
RemoveMetaServiceGroupStatus on a known group via the storage API, then verify
the status is cleared and later re-adding the same group does not inherit stale
AssignmentCount or Enabled values. Use the existing checkStatus helper and the
MetaServiceGroupStatus storage methods to locate the right behavior.

In `@tools/pd-ctl/tests/metaservicegroup/meta_service_group_test.go`:
- Around line 116-142: The rejected patch-command checks in meta-service-group
tests only verify error messages and do not confirm state was unchanged after
failure. In the test cases around ctl.GetRootCmd() and tests.MustExec for
set-enabled and set-assignment-count, re-list the target group after each
rejected command and assert the persisted status for etcd-group-0 still shows
enabled=false and assignment_count=10. Keep the checks alongside the existing
error assertions so any partial mutation in the patch flow is caught.
🪄 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: 6a2dab0f-4f3a-4a9a-a426-961150b51ebd

📥 Commits

Reviewing files that changed from the base of the PR and between 10f7107 and 529cb1c.

📒 Files selected for processing (12)
  • pkg/keyspace/keyspace_test.go
  • pkg/keyspace/meta_service_group.go
  • pkg/keyspace/meta_service_group_test.go
  • pkg/keyspace/util.go
  • pkg/storage/endpoint/meta_service_group.go
  • pkg/storage/meta_service_group_test.go
  • pkg/utils/keypath/absolute_key_path.go
  • server/apiv2/handlers/meta_service_group.go
  • tests/server/apiv2/handlers/meta_service_group_test.go
  • tests/server/apiv2/handlers/testutil.go
  • tools/pd-ctl/pdctl/command/meta_service_group_command.go
  • tools/pd-ctl/tests/metaservicegroup/meta_service_group_test.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/server/apiv2/handlers/testutil.go
  • pkg/keyspace/util.go
  • tools/pd-ctl/pdctl/command/meta_service_group_command.go
  • pkg/utils/keypath/absolute_key_path.go
  • tests/server/apiv2/handlers/meta_service_group_test.go

Comment thread pkg/keyspace/meta_service_group.go
Comment thread server/apiv2/handlers/meta_service_group.go
bufferflies and others added 2 commits June 30, 2026 15:33
- Skip meta-service group assignment when no enabled group exists instead
  of failing keyspace creation, mirroring the empty-group-set fallback.
- Reject moving a keyspace into a disabled group on config update
  (new ErrMetaServiceGroupDisabled, mapped to 400).
- Map errs.ErrEtcdTxnConflict to HTTP 409 on the meta-service group patch
  endpoints, since a lost etcd txn compare is a retryable conflict.
- Reject negative count in AssignToGroup; synthesize a default status in
  the list response so the enabled state is never dropped; add the missing
  Swagger {id} path param.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: tongjian <1045931706@qq.com>
…eassign

Cover AssignToGroup rejecting a negative count and reassignKeyspaceLocked
rejecting a disabled or unknown target group.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: tongjian <1045931706@qq.com>
@bufferflies

Copy link
Copy Markdown
Contributor Author

/retest

Comment thread pkg/keyspace/meta_service_group.go Outdated
func (m *MetaServiceGroupManager) updateAssignmentTxn(txn kv.Txn, oldGroupID, newGroupID string) error {
if oldGroupID != "" {
if err := m.store.IncrementAssignmentCount(txn, oldGroupID, -1); err != nil {
statusMap, err := m.store.LoadMetaServiceGroupStatus(txn, m.metaServiceGroups)

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 still reads m.metaServiceGroups, but callers like RemoveKeyspace reach updateAssignmentTxn without holding the meta-service group lock. Please load only oldGroupID/newGroupID or require the lock at every call site.

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.

Fixed in 82c8560: updateAssignmentTxn no longer reads the shared m.metaServiceGroups map. It now loads only the affected old/new group status by id (new loadGroupStatus helper), so unlocked callers like RemoveKeyspace no longer race with UpdateGroupsSafely. The old group's decrement is only persisted when it still has assignments, so a deleted group's status key isn't recreated. Verified with -race.

updateAssignmentTxn read the shared m.metaServiceGroups map, but callers
like RemoveKeyspace reach it without holding the meta-service group lock,
racing with UpdateGroupsSafely. Load only the affected old/new group
status by id instead, and only persist the old group's decrement when it
still has assignments so a deleted group's status is not recreated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: tongjian <1045931706@qq.com>
@bufferflies
bufferflies requested a review from rleungx July 1, 2026 02:56
Group IDs could contain '/', so such a group could be created and listed
but never patched or enabled via /meta-service-groups/{id}/status. Restrict
IDs to URL-safe characters ([A-Za-z0-9_-]) in AdjustMetaServiceGroups and
reject them with 400 in the add/update handler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: tongjian <1045931706@qq.com>
@bufferflies

Copy link
Copy Markdown
Contributor Author

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

@ti-chi-bot ti-chi-bot Bot added the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label Jul 1, 2026
@bufferflies

Copy link
Copy Markdown
Contributor Author

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

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

the rest LGTM

Comment thread server/config/config.go Outdated
// characters so that a group is always addressable via the
// /meta-service-groups/{id}/status endpoint (a '/' would otherwise split the
// path and make the group impossible to patch).
var metaServiceGroupIDPattern = regexp.MustCompile(`^[A-Za-z0-9_-]{1,64}$`)

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 new validation can break existing configurations.

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 you confirm whether all current meta-service group IDs already satisfy this new format? If yes, this should be fine; otherwise we may need to only reject /, or encode/escape the path parameter instead.

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.

Good question. The only IDs currently in use in tests/config are like group-0/etcd-group-0, which are fine either way. To be safe against existing IDs I relaxed the validation in 94c8a93 to reject only / (the character that would break the /{id}/status path); everything else (., :, etc.) is left untouched. This keeps @rleungx's addressability requirement while not risking any existing group ID.

Comment thread pkg/keyspace/meta_service_group.go Outdated
m.RLock()
defer m.RUnlock()
return m.store.RunInTxn(ctx, func(txn kv.Txn) error {
statusMap, err := m.store.LoadMetaServiceGroupStatus(txn, m.metaServiceGroups)

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.

A small efficiency/conflict-scope question: when patching one group's status, this currently loads every group's status. With the etcd backend, all loaded status keys become transaction compare conditions, so patching group-a may return 409 if group-b is concurrently updated by keyspace assignment.

Would it be better to first check m.metaServiceGroups[groupID] under the lock to validate existence, then load only the target groupID's status? That would keep the read/write set smaller and avoid conflicts caused by unrelated group updates.

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.

Good point. Fixed in 9ee11c3: PatchStatus now validates m.metaServiceGroups[groupID] under the read lock and then loads only the target group's status in the txn (via loadGroupStatus), so the etcd compare set is limited to that one key and a concurrent assignment on another group no longer triggers a spurious 409.

@@ -0,0 +1,143 @@
// Copyright 2026 TiKV Project Authors.

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 adds tools/pd-ctl/tests/metaservicegroup, but the repo already has tools/pd-ctl/tests/meta_service_group. Splitting tests for the same pd-ctl command across two differently named directories makes them easy to miss during future maintenance. Please move these new tests into the existing meta_service_group directory.

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.

Fixed in 9ee11c3: moved the set-enabled/set-assignment-count tests into the existing tools/pd-ctl/tests/meta_service_group suite (as TestSetEnabled/TestSetAssignmentCount/TestSetStatusInvalidInput) and removed the duplicate metaservicegroup directory.

… tests

PatchStatus loaded every group's status, widening the etcd txn compare set
so a patch could fail with a spurious conflict when an unrelated group was
concurrently updated. Validate existence against the in-memory group set
under the lock, then load only the target group's status in the txn.

Also move the set-enabled/set-assignment-count pd-ctl tests into the
existing tools/pd-ctl/tests/meta_service_group suite instead of a second
directory with a near-duplicate name.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: tongjian <1045931706@qq.com>
re.Contains(string(output), "address cannot contain empty segment")
}

func (suite *metaServiceGroupCLITestSuite) findGroup(groups []*handlers.MetaServiceGroupStatus, id string) *handlers.MetaServiceGroupStatus {

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.

plz fix lint

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.

Fixed in 94c8a93: findGroup is now a free function instead of a suite method (it didn't use the receiver), which clears the unused-receiver lint.

Comment thread server/config/config.go Outdated
// characters so that a group is always addressable via the
// /meta-service-groups/{id}/status endpoint (a '/' would otherwise split the
// path and make the group impossible to patch).
var metaServiceGroupIDPattern = regexp.MustCompile(`^[A-Za-z0-9_-]{1,64}$`)

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 you confirm whether all current meta-service group IDs already satisfy this new format? If yes, this should be fine; otherwise we may need to only reject /, or encode/escape the path parameter instead.

Relax the group ID validation to reject only '/' (the character that would
make a group unpatchable via /meta-service-groups/{id}/status) instead of a
strict whitelist, so existing IDs containing '.', ':' etc. are not broken.
Also make the pd-ctl test findGroup a free function to satisfy the
unused-receiver linter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: tongjian <1045931706@qq.com>
cmd.PrintErrln("Failed to marshal request:", err)
return
}
resp, err := doRequest(cmd, metaServiceGroupPrefix+"/"+groupID+"/status", http.MethodPatch,

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 still concatenates the raw group ID into the URL path. Since the server now only rejects /, IDs containing ?, #, %, or whitespace can be created via the JSON patch API but cannot be reliably enabled through pd-ctl because those characters are parsed as URL syntax before reaching the handler. The same applies to set-assignment-count below.

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.

Fixed in 5fcc61b: both set-enabled and set-assignment-count now wrap the group ID with url.PathEscape when building the request path, so ?, #, % and whitespace are percent-encoded instead of being parsed as URL syntax. gin decodes the :id path param on the server side, so the round trip is exact. Added a pd-ctl test that creates a group with a special-character ID (group a?b#c%d) and enables it through the CLI.

The server now only rejects '/', so a group ID may contain '?', '#', '%'
or whitespace. pd-ctl concatenated the raw ID into the request path, where
those characters are parsed as URL syntax and never reach the handler.
Escape the ID with url.PathEscape (gin decodes the path param on the other
side) so set-enabled/set-assignment-count work for any valid ID.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: tongjian <1045931706@qq.com>
@bufferflies
bufferflies requested a review from lhy1024 July 2, 2026 07:03
@ti-chi-bot ti-chi-bot Bot added lgtm and removed needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels Jul 2, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

[LGTM Timeline notifier]

Timeline:

  • 2026-07-01 07:51:35.655149921 +0000 UTC m=+198637.355529334: ☑️ agreed by rleungx.
  • 2026-07-02 07:47:16.728734934 +0000 UTC m=+284778.429114357: ☑️ agreed by lhy1024.

@bufferflies

Copy link
Copy Markdown
Contributor Author

/retest

if countVal == "" {
return 0, nil
status := &MetaServiceGroupStatus{}
if statusVal == "" {

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.

On upgrade, existing clusters may only have the old /assignment_count key. Returning an empty disabled status here makes assigned_keyspaces drop to 0 and makes all existing groups ineligible for automatic assignment until status is manually patched, so the PR loses the persisted assignment state during the status-key migration.

Comment thread server/config/config.go
if id == "" {
return errors.New("[keyspace] meta-service group ID cannot be empty")
}
if !IsValidMetaServiceGroupID(id) {

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.

Existing configs can already contain / in a meta-service group ID. Since this validation runs in the startup/config adjust path, such a cluster fails validation on upgrade before it can use the API delete escape hatch, so this is still a config compatibility break for legacy IDs.

@bufferflies

Copy link
Copy Markdown
Contributor Author

/retest

@ti-chi-bot

ti-chi-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: AmoebaProtozoa, lhy1024, niubell, 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 the approved label Jul 3, 2026
@bufferflies

Copy link
Copy Markdown
Contributor Author

/retest

1 similar comment
@bufferflies

Copy link
Copy Markdown
Contributor Author

/retest

@ti-chi-bot
ti-chi-bot Bot merged commit 1de3200 into tikv:master Jul 3, 2026
74 of 87 checks passed
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-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.

5be7eae7 add meta-service-group status

5 participants