Skip to content

server: add microservice metadata cleanup API - #11141

Open
rleungx wants to merge 7 commits into
tikv:masterfrom
rleungx:cleanup-microservice-metadata
Open

server: add microservice metadata cleanup API#11141
rleungx wants to merge 7 commits into
tikv:masterfrom
rleungx:cleanup-microservice-metadata

Conversation

@rleungx

@rleungx rleungx commented Aug 13, 2026

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: Close #10990

After PD switches from microservice mode to PD mode, stale TSO member
assignments can remain in the persisted default keyspace group and block a
later switch back to microservice mode.

This is an independent, master-based alternative to #10996. It uses an explicit
synchronous admin operation instead of leader-start cleanup, so cleanup does not
delay leader readiness in PD mode and the mode-switch controller has an explicit
completion barrier.

What is changed and how does it work?

Add an admin API available only on the serving leader in PD mode that clears
only the Members field of the persisted default TSO keyspace group.

Fence every successful response, including no-op responses, with the exact PD
leadership value and lease, the default group revision, and the absence of any
non-default group. Reject cleanup when a non-default group exists or the default
group is missing, splitting, or merging.

Preserve the default group and all its other fields, keyspace assignment
markers, and TSO timestamp data. Do not add a background cleanup worker,
durable cleanup marker, or TSO fallback behavior.

Add unit, HTTP API, and repeated mode-switch integration coverage.

The endpoint is POST /pd/api/v1/admin/microservice/metadata/cleanup:

  • 200 returns {"changed": true|false} after the fenced transaction commits.
  • 409 means the current mode or keyspace-group topology is not safe to clean.
  • 503 means leadership or metadata changed concurrently; the request is safe
    to retry on the serving leader in PD mode.

The external mode-switch sequence is part of the contract:

  1. In microservice mode, merge every keyspace group and timeline into group 0,
    and wait for all split and merge operations to finish.
  2. Stop or otherwise fence TSO and every old API-mode metadata writer.
  3. Start PD in PD mode and wait for a serving leader.
  4. Call the cleanup endpoint and require 200 before proceeding.
  5. Keep old writers fenced, stop PD in PD mode, start API-mode PD, and then start
    TSO.

A 200 response is a linearizable point-in-time certificate, not a durable
write fence. A late old API-mode writer can make the metadata stale again, so
callers must enforce the ordering above. During rolling upgrades, callers must
also ensure the serving leader supports this endpoint; an old leader returns
404.

The endpoint is intentionally named around microservice metadata so more
explicitly safe cleanup operations can be added later. Its initial behavior is
deliberately limited to group 0's persisted Members.

Check List

Tests

  • Unit test
  • Integration test

Validation status:

  • Formatting, diff, package loading, and static vet checks passed.
  • Project unit and integration suites were not run locally; CI validation is
    pending.

Code changes

Release note

Add an administrative API to clear stale default TSO keyspace-group members
before switching from PD mode back to microservice mode.

Summary by CodeRabbit

  • New Features

    • Added an administrative API for cleaning up stale microservice metadata.
    • Cleanup preserves the default keyspace configuration while removing obsolete membership data.
    • The API reports whether metadata changed and supports safe, repeatable requests.
  • Bug Fixes

    • Added safeguards against cleanup during unsafe states, leadership changes, concurrent updates, or unavailable service conditions.
    • Improved stale metadata handling during PD/TSO mode transitions and restarts.

Add a synchronous admin endpoint that clears stale TSO keyspace group member assignments in normal PD mode. Fence the cleanup with the exact PD leadership term and keyspace group revisions while preserving durable topology, assignment markers, and timestamps.

Add unit, API, and mode-switch coverage.

Signed-off-by: Ryan Leung <rleungx@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 Aug 13, 2026
@ti-chi-bot

ti-chi-bot Bot commented Aug 13, 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 rleungx 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

@ti-chi-bot ti-chi-bot Bot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Aug 13, 2026
@coderabbitai

coderabbitai Bot commented Aug 13, 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cf46f688-3726-4c5e-90f4-bc2a7fb067d8

📥 Commits

Reviewing files that changed from the base of the PR and between c18b65f and d2824bf.

📒 Files selected for processing (3)
  • server/api/admin.go
  • server/microservice_cleanup_test.go
  • server/server.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • server/api/admin.go
  • server/server.go
  • server/microservice_cleanup_test.go

📝 Walkthrough

Walkthrough

Adds a fenced server operation that clears stale microservice metadata, exposes it through a POST admin endpoint, and tests leadership, concurrency, mode-switch, keyspace-group, and timestamp behavior.

Changes

Microservice metadata cleanup

Layer / File(s) Summary
Fenced cleanup operation
server/server.go, server/microservice_cleanup_test.go
The server validates mode, leadership, and keyspace-group state before clearing persisted members through a fenced etcd transaction. Tests cover rejection, leadership changes, concurrent updates, and no-op cleanup.
Admin cleanup endpoint
server/api/admin.go, server/api/router.go, tests/server/api/admin_test.go
A POST endpoint invokes cleanup, maps classified errors to HTTP responses, and returns whether metadata changed. API tests cover method restrictions, cleanup, preservation, and idempotence.
Mode-switch integration coverage
tests/integrations/mcs/tso/server_test.go
Integration tests cover PD and TSO mode switches, PD restarts, cleanup requests, keyspace-group membership, and strictly increasing timestamps.

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

Mergeability Score: 🔵 Low · up to d2824

The cleanup API can return 503 for a healthy request when the initial metadata read consumes the shared deadline, causing avoidable retries during mode switches. The change is otherwise mergeable with explicit owner awareness or follow-up to separate or correctly budget the read and transaction deadlines.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AdminAPI
  participant Server
  participant Etcd
  Client->>AdminAPI: POST /api/v1/admin/microservice/metadata/cleanup
  AdminAPI->>Server: CleanupMicroserviceMetadata
  Server->>Etcd: Read leadership and keyspace-group metadata
  Server->>Etcd: Commit fenced member removal
  Etcd-->>Server: Commit result
  Server-->>AdminAPI: Changed status or classified error
  AdminAPI-->>Client: JSON response or HTTP error
Loading

Possibly related PRs

  • tikv/pd#10478: Covers related TSO/PD dynamic mode-switch and microservice lifecycle behavior.
  • tikv/pd#10996: Also modifies microservice metadata cleanup in server/server.go and related tests.

Suggested labels: contribution

Suggested reviewers: jmpotato, lhy1024

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the server package change and the new microservice metadata cleanup API.
Description check ✅ Passed The description covers the issue, implementation, API behavior, tests, validation status, HTTP and persistent-data changes, and release note.
Linked Issues check ✅ Passed The implementation directly addresses issue #10990 by clearing stale default keyspace-group member assignments during the mode switch.
Out of Scope Changes check ✅ Passed The implementation, route, unit tests, API tests, and mode-switch integration tests are related to the stated cleanup objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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.

@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 (5)
tests/integrations/mcs/tso/server_test.go (3)

974-974: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Put ctx first in waitForTSOMonotonic.

waitForTSOMonotonic(re *require.Assertions, ctx context.Context, ...) takes the context as the second parameter. checkTSOMonotonic in the same file takes ctx first. Move ctx to the first position for consistency and to satisfy the context-first convention.

As per coding guidelines: "First parameter must be context.Context for external effects; never store contexts in structs".

♻️ Proposed change
-func waitForTSOMonotonic(re *require.Assertions, ctx context.Context, client pd.Client, globalLastTS *uint64) {
+func waitForTSOMonotonic(ctx context.Context, re *require.Assertions, client pd.Client, globalLastTS *uint64) {

Update the four call sites at Lines 834, 835, 855, and 856 accordingly.

🤖 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/tso/server_test.go` at line 974, Update
waitForTSOMonotonic to accept ctx as its first parameter, matching
checkTSOMonotonic and the context-first convention, then adjust all four call
sites to pass the arguments in the new order.

Source: Coding guidelines


890-894: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reuse the exported response type instead of a local struct.

cleanupMicroserviceMetadataViaHTTP declares an anonymous struct with a changed JSON tag. server/api.CleanupMicroserviceMetadataResponse already defines this wire contract, and tests/server/api/admin_test.go uses it. Reuse the exported type so a JSON tag change fails both tests.

🤖 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/tso/server_test.go` around lines 890 - 894, Update
cleanupMicroserviceMetadataViaHTTP to unmarshal into the exported
server/api.CleanupMicroserviceMetadataResponse type instead of its local
anonymous struct, preserving the existing result.Changed return behavior and
shared JSON contract.

982-982: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use tsoutil.ComposeTS for the timestamp composition.

The expression (uint64(physical) << 18) + uint64(logical) repeats the logical-bit shift. The file already imports tsoutil and uses tsoutil.ComposeTS at Line 819. Call tsoutil.ComposeTS(physical, logical) here so the shift constant stays in one place.

🤖 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/tso/server_test.go` at line 982, Replace the manual
timestamp composition in the test with tsoutil.ComposeTS(physical, logical),
reusing the existing imported helper and matching the usage already present near
line 819.
server/api/admin.go (1)

61-71: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Regenerate the Swagger spec for the new annotations.

This change adds new @Router and @Success annotations. Run make swagger-spec with SWAGGER=1 so the committed spec matches the code.

As per coding guidelines: "Regenerate Swagger spec with make swagger-spec (SWAGGER=1) when API annotations change".

🤖 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 `@server/api/admin.go` around lines 61 - 71, Regenerate the committed Swagger
specification for the updated annotations on CleanupMicroserviceMetadata by
running make swagger-spec with SWAGGER=1, ensuring the new `@Router` and `@Success`
definitions are reflected in the generated spec.

Source: Coding guidelines

server/microservice_cleanup.go (1)

140-147: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Give the fenced transaction its own request timeout.

The cleanup operation currently reuses the request context created for the initial metadata read. A slow read, or the test failpoint that blocks before commit, can consume the shared deadline and cause the transaction to return a generic unavailable error before the intended compare-and-fence path is exercised. Derive a fresh timeout context for the commit so the transaction receives its own request budget.

🤖 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 `@server/microservice_cleanup.go` around lines 140 - 147, Update the
transaction commit path around kv.NewSlowLogTxnWithContext to use a separate
context with its own etcdutil.DefaultRequestTimeout budget, derived after the
existing Get context is used. Keep operationCtx for the read and pass the new
transaction-specific context to the fenced transaction.

Apply the same fix in `@server/microservice_cleanup_test.go` around lines 176 -
199: The blocked-commit test can otherwise expire the shared operation deadline
and mask the fencing behavior.
🤖 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 `@server/api/admin.go`:
- Around line 61-71: Regenerate the committed Swagger specification for the
updated annotations on CleanupMicroserviceMetadata by running make swagger-spec
with SWAGGER=1, ensuring the new `@Router` and `@Success` definitions are reflected
in the generated spec.

In `@server/microservice_cleanup.go`:
- Around line 140-147: Update the transaction commit path around
kv.NewSlowLogTxnWithContext to use a separate context with its own
etcdutil.DefaultRequestTimeout budget, derived after the existing Get context is
used. Keep operationCtx for the read and pass the new transaction-specific
context to the fenced transaction.

Apply the same fix in `@server/microservice_cleanup_test.go` around lines 176 -
199: The blocked-commit test can otherwise expire the shared operation deadline
and mask the fencing behavior.

In `@tests/integrations/mcs/tso/server_test.go`:
- Line 974: Update waitForTSOMonotonic to accept ctx as its first parameter,
matching checkTSOMonotonic and the context-first convention, then adjust all
four call sites to pass the arguments in the new order.
- Around line 890-894: Update cleanupMicroserviceMetadataViaHTTP to unmarshal
into the exported server/api.CleanupMicroserviceMetadataResponse type instead of
its local anonymous struct, preserving the existing result.Changed return
behavior and shared JSON contract.
- Line 982: Replace the manual timestamp composition in the test with
tsoutil.ComposeTS(physical, logical), reusing the existing imported helper and
matching the usage already present near line 819.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 371f67f6-7ebe-4159-b323-bf6604475a2a

📥 Commits

Reviewing files that changed from the base of the PR and between 3430f76 and 5ee8d82.

📒 Files selected for processing (6)
  • server/api/admin.go
  • server/api/router.go
  • server/microservice_cleanup.go
  • server/microservice_cleanup_test.go
  • tests/integrations/mcs/tso/server_test.go
  • tests/server/api/admin_test.go

Fail closed when the default TSO keyspace group is missing so the cleanup API cannot certify an ambiguous assignment state. Preserve revision fencing when the group is deleted concurrently.

Fix the mode-switch integration test input and static-check issues, and cover the missing-group HTTP behavior.

Signed-off-by: Ryan Leung <rleungx@gmail.com>
@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.62937% with 42 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.43%. Comparing base (3430f76) to head (e302f55).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff            @@
##           master   #11141    +/-   ##
========================================
  Coverage   79.43%   79.43%            
========================================
  Files         542      542            
  Lines       77117    77260   +143     
========================================
+ Hits        61259    61375   +116     
- Misses      11571    11584    +13     
- Partials     4287     4301    +14     
Flag Coverage Δ
unittests 79.43% <70.62%> (+<0.01%) ⬆️

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.

Signed-off-by: Ryan Leung <rleungx@gmail.com>
Signed-off-by: Ryan Leung <rleungx@gmail.com>
Signed-off-by: Ryan Leung <rleungx@gmail.com>
Rewrite only the members field in the persisted keyspace group JSON so additive fields written by newer versions survive cleanup. Reject ambiguous or malformed group objects before returning a successful cleanup certificate.

Signed-off-by: Ryan Leung <rleungx@gmail.com>
@rleungx

rleungx commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

/retest

Signed-off-by: Ryan Leung <rleungx@gmail.com>
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.

PD microservice mode switch blocked

1 participant