Skip to content

keyspace: avoid loading all groups every second for node allocation#11026

Open
rleungx wants to merge 3 commits into
tikv:masterfrom
rleungx:fix-559
Open

keyspace: avoid loading all groups every second for node allocation#11026
rleungx wants to merge 3 commits into
tikv:masterfrom
rleungx:fix-559

Conversation

@rleungx

@rleungx rleungx commented Jul 20, 2026

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: close #11027

The API leader periodically loaded every keyspace group from etcd to repair TSO node membership. With millions of keyspaces, this creates recurring etcd reads, JSON decoding, allocation, and GC pressure, and can affect availability.

What is changed and how does it work?

  • Trigger reconciliation from TSO-node and group-membership changes instead of a one-second full scan.
  • Keep a lightweight membership cache; healthy groups perform no etcd read.
  • For a possibly under-replicated group, transactionally load and revalidate only that group before updating it.
  • Fence watcher and reconciliation work by API-leader term.
  • Rebuild the full business cache on each initial-load retry, while incremental watch events decode only ID and members.
  • Preserve public APIs and persisted JSON formats.

Original TiDB Cloud issue: https://github.com/tidbcloud/pd-cse/issues/559
Companion TiDB Cloud PR: https://github.com/tidbcloud/pd-cse/pull/567

Check List

Tests

  • Unit test
  • Integration test
  • Race test
  • Benchmark with one million keyspaces

Benchmark: the healthy event-driven path performs 0 etcd read bytes/op; membership-only watch processing does not decode the keyspace list.

Code changes

  • No configuration change
  • No HTTP API change
  • No persistent data change

Side effects

  • No breaking backward compatibility
  • No expected performance regression

Release note

Reduce PD etcd reads and memory pressure when assigning TSO nodes to keyspace groups.

Summary by CodeRabbit

  • Performance
    • Improved keyspace-group reconciliation by switching to an event-driven, leader-fenced allocation flow based on membership changes.
    • Reduced unnecessary keyspace-group storage reads/writes and improved how node allocation reacts to membership and TSO node updates.
  • Reliability
    • Made default keyspace-group updates more consistent and added safer reconciliation fallback behavior for partial/invalid loads.
    • Ensured canceled transactions do not persist writes to storage.
  • Tests
    • Expanded reconciliation/watcher coverage with additional behavioral checks and added a benchmark comparing legacy vs event-driven reconciliation.

@ti-chi-bot ti-chi-bot Bot added do-not-merge/needs-linked-issue 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. do-not-merge/needs-triage-completed labels Jul 20, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jul 20, 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 disksing 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 Jul 20, 2026
@coderabbitai

coderabbitai Bot commented Jul 20, 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: 1ec7b016-24fc-487c-970c-383c326a45d8

📥 Commits

Reviewing files that changed from the base of the PR and between 824d166 and 95497a2.

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

📝 Walkthrough

Walkthrough

Keyspace-group allocation now uses leader-term-scoped, watcher-triggered reconciliation with cached memberships and revalidation. Cache writes use shared locked helpers. Etcd transaction reads now honor transaction contexts, with cancellation coverage added.

Changes

Keyspace reconciliation

Layer / File(s) Summary
Reconciliation bootstrap and state
pkg/keyspace/tso_keyspace_group.go
Bootstrap and manager state support reconciliation terms, membership caches, watcher setup, and storage fallback.
Watcher-driven reconciliation
pkg/keyspace/tso_keyspace_group.go
Membership and TSO-node changes notify active reconciliation loops, which target potentially under-replicated groups.
Allocation and cache integration
pkg/keyspace/tso_keyspace_group.go, pkg/keyspace/keyspace.go
Allocation revalidates stored memberships, and keyspace-group write paths use shared locked cache helpers.
Reconciliation validation and benchmarks
pkg/keyspace/tso_keyspace_group_test.go
Tests and benchmarks cover watcher retries, term fencing, allocation triggers, transient failures, and large-scale reconciliation.

Context-aware etcd transactions

Layer / File(s) Summary
Context-aware transaction reads
pkg/utils/etcdutil/etcdutil.go, pkg/storage/kv/etcd_kv.go, pkg/storage/kv/kv_test.go
Adds context-aware etcd reads, uses transaction contexts in Load, and tests canceled transactions.

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

Sequence Diagram(s)

sequenceDiagram
  participant TSONodesWatcher
  participant KeyspaceGroupWatcher
  participant GroupManager
  participant EtcdStorage
  TSONodesWatcher->>GroupManager: notify TSO membership change
  KeyspaceGroupWatcher->>GroupManager: apply keyspace-group membership change
  GroupManager->>EtcdStorage: load candidate group
  EtcdStorage-->>GroupManager: return persisted membership
  GroupManager->>GroupManager: revalidate active reconciliation term
  GroupManager->>EtcdStorage: save updated membership
Loading

Possibly related PRs

  • tikv/pd#10932: Updates auto-split patrol behavior that calls keyspace-group split methods changed here.

Suggested labels: ok-to-test, lgtm, approved

Suggested reviewers: lhy1024, bufferflies, amoebaprotozoa

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: event-driven keyspace group allocation instead of periodic full loading.
Description check ✅ Passed The description follows the template and includes the issue link, change summary, tests, checklist items, and release note.
Linked Issues check ✅ Passed The PR satisfies #11027 by making reconciliation event-driven, avoiding healthy-group reads, and revalidating only potentially under-replicated groups.
Out of Scope Changes check ✅ Passed The changes stay within the stated scope; supporting refactors and tests all serve the keyspace-group reconciliation redesign.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
✨ 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.

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

@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 (1)
pkg/keyspace/tso_keyspace_group.go (1)

151-153: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Use errors.Is for the sentinel check.

err != errs.ErrKeyspaceGroupExists only works while saveKeyspaceGroups returns the sentinel unwrapped. It silently breaks if the error is ever wrapped (Wrap/FastGenByArgs).

♻️ Suggested change
-	if err != nil && err != errs.ErrKeyspaceGroupExists {
+	if err != nil && !errors.Is(err, errs.ErrKeyspaceGroupExists) {
 		return err
 	}

As per coding guidelines: "Use errors.Is/As for sentinel error checks".

🤖 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/tso_keyspace_group.go` around lines 151 - 153, Update the
sentinel comparison in saveKeyspaceGroups to use errors.Is, preserving the
existing behavior of returning errors other than ErrKeyspaceGroupExists while
recognizing wrapped instances of that sentinel.

Source: Coding guidelines

🤖 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/storage/kv/kv_test.go`:
- Around line 49-58: Update testCanceledTxn to invoke txn.Load using the
already-canceled context before or alongside txn.Save, and assert the load
returns an error. Keep the existing transaction cancellation and
post-transaction key assertions unchanged so the test covers both txn.Load
cancellation and commit behavior.

---

Nitpick comments:
In `@pkg/keyspace/tso_keyspace_group.go`:
- Around line 151-153: Update the sentinel comparison in saveKeyspaceGroups to
use errors.Is, preserving the existing behavior of returning errors other than
ErrKeyspaceGroupExists while recognizing wrapped instances of that sentinel.
🪄 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: a2683cc6-76ac-41a1-a3b1-fd0ffda8e79f

📥 Commits

Reviewing files that changed from the base of the PR and between 0b37df9 and 1746296.

📒 Files selected for processing (6)
  • pkg/keyspace/keyspace.go
  • pkg/keyspace/tso_keyspace_group.go
  • pkg/keyspace/tso_keyspace_group_test.go
  • pkg/storage/kv/etcd_kv.go
  • pkg/storage/kv/kv_test.go
  • pkg/utils/etcdutil/etcdutil.go

Comment thread pkg/storage/kv/kv_test.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

🤖 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/tso_keyspace_group_test.go`:
- Around line 238-262: Defer cancelOldLeader immediately after creating
oldLeaderCtx in the reconcile-term test, ensuring cleanup occurs on every exit
path including t.Fatal failures. Remove the trailing cancelOldLeader call after
the notification assertions, while preserving the existing deferred
cancelNewLeader cleanup.
🪄 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: 7af3fe4a-6d9e-4db9-883e-70509f0bb122

📥 Commits

Reviewing files that changed from the base of the PR and between 1746296 and d8729c2.

📒 Files selected for processing (6)
  • pkg/keyspace/keyspace.go
  • pkg/keyspace/tso_keyspace_group.go
  • pkg/keyspace/tso_keyspace_group_test.go
  • pkg/storage/kv/etcd_kv.go
  • pkg/storage/kv/kv_test.go
  • pkg/utils/etcdutil/etcdutil.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • pkg/storage/kv/kv_test.go
  • pkg/storage/kv/etcd_kv.go
  • pkg/keyspace/keyspace.go
  • pkg/utils/etcdutil/etcdutil.go
  • pkg/keyspace/tso_keyspace_group.go

Comment thread pkg/keyspace/tso_keyspace_group_test.go Outdated
Signed-off-by: Ryan Leung <rleungx@gmail.com>

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

Caution

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

⚠️ Outside diff range comments (1)
pkg/keyspace/tso_keyspace_group_test.go (1)

526-528: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Defer manager.Close() to prevent resource leaks.

The KeyspaceGroupManager is created but its Close method is never deferred in this sub-benchmark. Omitting this cleanup can leak background goroutines and contexts across the benchmark run.

🧹 Proposed fix
 	b.Run("watch-membership-only", func(b *testing.B) {
 		manager := NewKeyspaceGroupManager(ctx, store, nil)
+		defer manager.Close()
 		manager.groupWatcherTerm = 1
🤖 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/tso_keyspace_group_test.go` around lines 526 - 528, Defer
cleanup for the KeyspaceGroupManager created in the “watch-membership-only”
benchmark by calling manager.Close() immediately after NewKeyspaceGroupManager
returns, ensuring its goroutines and contexts are released when the
sub-benchmark exits.
🤖 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.

Outside diff comments:
In `@pkg/keyspace/tso_keyspace_group_test.go`:
- Around line 526-528: Defer cleanup for the KeyspaceGroupManager created in the
“watch-membership-only” benchmark by calling manager.Close() immediately after
NewKeyspaceGroupManager returns, ensuring its goroutines and contexts are
released when the sub-benchmark exits.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f7860217-ea8e-4e80-a694-61d4807b767a

📥 Commits

Reviewing files that changed from the base of the PR and between d8729c2 and 824d166.

📒 Files selected for processing (3)
  • pkg/keyspace/tso_keyspace_group.go
  • pkg/keyspace/tso_keyspace_group_test.go
  • pkg/storage/kv/kv_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/storage/kv/kv_test.go
  • pkg/keyspace/tso_keyspace_group.go

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

ti-chi-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

@rleungx: 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 95497a2 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.

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.

Avoid loading all keyspace groups every second for TSO node allocation

1 participant