Skip to content

Conversation

@lhy1024
Copy link
Contributor

@lhy1024 lhy1024 commented Dec 10, 2025

What problem does this PR solve?

Issue Number: Ref #9764

What is changed and how does it work?

Check List

Tests

  • Unit test

Release note

None.

Summary by CodeRabbit

  • New Features

    • Added API endpoints to list and fetch affinity groups; introduced a watcher to sync affinity groups and label rules from cluster state; added microservice redirect middleware for affinity requests.
  • Bug Fixes

    • Improved handling of label rules that arrive before their affinity group by buffering and applying them when the group appears.
  • Tests

    • Added comprehensive watcher/manager tests and a unit suite for redirect logic; re-enabled affinity forwarding header test.

✏️ Tip: You can customize this high-level summary in your review settings.

@ti-chi-bot ti-chi-bot bot added 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. labels Dec 10, 2025
@ti-chi-bot
Copy link
Contributor

ti-chi-bot bot commented Dec 10, 2025

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 the dco-signoff: yes Indicates the PR's author has signed the dco. label Dec 10, 2025
@ti-chi-bot
Copy link
Contributor

ti-chi-bot bot commented Dec 10, 2025

[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 nolouch 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 needs-cherry-pick-release-8.5 Should cherry pick this PR to release-8.5 branch. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Dec 10, 2025
@codecov
Copy link

codecov bot commented Dec 10, 2025

Codecov Report

❌ Patch coverage is 80.54299% with 43 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.65%. Comparing base (09f1b5d) to head (c869c8d).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #10042      +/-   ##
==========================================
+ Coverage   78.51%   78.65%   +0.14%     
==========================================
  Files         513      517       +4     
  Lines       68893    69393     +500     
==========================================
+ Hits        54091    54584     +493     
+ Misses      10887    10862      -25     
- Partials     3915     3947      +32     
Flag Coverage Δ
unittests 78.65% <80.54%> (+0.14%) ⬆️

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.

@lhy1024 lhy1024 force-pushed the affinity-pr6 branch 2 times, most recently from 079ad98 to 4dc8ea5 Compare December 11, 2025 10:07
@lhy1024 lhy1024 marked this pull request as ready for review December 11, 2025 10:07
@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 Dec 11, 2025
@lhy1024
Copy link
Contributor Author

lhy1024 commented Dec 11, 2025

/retest

1 similar comment
@lhy1024
Copy link
Contributor Author

lhy1024 commented Dec 12, 2025

/retest

@lhy1024 lhy1024 force-pushed the affinity-pr6 branch 2 times, most recently from 2779204 to 2c87924 Compare December 13, 2025 14:24
Signed-off-by: lhy1024 <admin@liudos.us>
Signed-off-by: lhy1024 <admin@liudos.us>
Signed-off-by: lhy1024 <admin@liudos.us>
lhy1024 added a commit to lhy1024/pd that referenced this pull request Dec 14, 2025
Signed-off-by: lhy1024 <admin@liudos.us>
lhy1024 added a commit to lhy1024/pd that referenced this pull request Dec 14, 2025
Signed-off-by: lhy1024 <admin@liudos.us>
lhy1024 added a commit to lhy1024/pd that referenced this pull request Dec 14, 2025
Signed-off-by: lhy1024 <admin@liudos.us>
lhy1024 added a commit to lhy1024/pd that referenced this pull request Dec 14, 2025
Signed-off-by: lhy1024 <admin@liudos.us>
lhy1024 added a commit to lhy1024/pd that referenced this pull request Dec 15, 2025
Signed-off-by: lhy1024 <admin@liudos.us>
Signed-off-by: lhy1024 <admin@liudos.us>
}

// AffinityMicroserviceRedirector only forwards affinity GET requests to the scheduling service.
func AffinityMicroserviceRedirector() gin.HandlerFunc {
Copy link
Member

Choose a reason for hiding this comment

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

Why not in server/api/server.go?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it is a middleware for v2

Signed-off-by: lhy1024 <admin@liudos.us>
@lhy1024
Copy link
Contributor Author

lhy1024 commented Dec 15, 2025

/retest

labelRule = m.labelRuleBuffer[group.ID]
}
// Once group created, the buffer must be deleted.
delete(m.labelRuleBuffer, group.ID)
Copy link
Member

Choose a reason for hiding this comment

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

Is it better to delete after updateGroupLabelRuleLockedWithCount?

return
}
// Attach only non-empty label rules to avoid marking the group as having ranges when it does not.
if len(gkr.KeyRanges) > 0 {
Copy link
Member

Choose a reason for hiding this comment

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

If len(keyRanges) is a default limit, can we put it inside updateGroupLabelRuleLockedWithCount

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moving the check inside would break the other call.

// Store LabelRule information in the buffer when it is synchronized before the group.
m.labelRuleBuffer[groupID] = labelRule
return nil
}
Copy link
Member

Choose a reason for hiding this comment

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

How about returning direct when !exist && len(gkr.KeyRanges) ==0?

_, err = client.Put(ctx, labelKey, string(labelValue))
re.NoError(err)

time.Sleep(100 * time.Millisecond)
Copy link
Member

Choose a reason for hiding this comment

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

It is better to use Eventually

Signed-off-by: lhy1024 <admin@liudos.us>
@lhy1024
Copy link
Contributor Author

lhy1024 commented Jan 8, 2026

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

This pull request introduces an etcd-based affinity group watcher for the scheduling service, exposes affinity groups via REST API endpoints, enhances the manager to buffer label rules arriving before their corresponding groups, adds a check-less variant for label rule parsing, and refactors microservice redirect middleware to use a unified matching function and new RedirectRule type.

Changes

Cohort / File(s) Summary
Affinity Watcher & Tests
pkg/mcs/scheduling/server/affinity/watcher.go, pkg/mcs/scheduling/server/affinity/watcher_test.go
New Watcher watches etcd for affinity groups and region label rules, unmarshals JSON, and syncs create/update/delete events with the affinity manager; lifecycle management and tests for lifecycle, filtering, pre-existing data, concurrency, and out-of-order arrivals.
Scheduling Server & API
pkg/mcs/scheduling/server/apis/v1/api.go, pkg/mcs/scheduling/server/server.go
New REST endpoints for affinity groups (/scheduling/api/v1/affinity-groups and /:group_id), helper to get affinity manager, and server wiring to create/close the affinity watcher during startup/shutdown.
Affinity Manager & Tests
pkg/schedule/affinity/manager.go, pkg/schedule/affinity/manager_test.go
Adds labelRuleBuffer to buffer label rules that arrive before groups; sync paths updated to attach buffered rules when groups appear and to clear buffers on deletes; test added for label-rule-before-group scenario.
Labeler Parsing Changes
pkg/schedule/labeler/rules.go, pkg/schedule/labeler/labeler.go, pkg/mcs/scheduling/server/rule/watcher.go
Introduces NewLabelRuleFromJSONWithoutCheck to parse label rules without invoking checkAndAdjust; NewLabelRuleFromJSON now explicitly calls checkAndAdjust; labeler and region-watcher now use the check-less constructor in some load paths.
Microservice Redirect Refactor & Tests
pkg/utils/apiutil/serverapi/middleware.go, pkg/utils/apiutil/serverapi/middleware_test.go, server/apiv2/middlewares/microservice_redirector.go, server/apiv2/router.go
Replaces internal redirect struct with exported RedirectRule, adds MatchMicroserviceRedirect to centralize matching, implements MicroserviceRedirector middleware and AffinityMicroserviceRedirector wrapper, updates middleware use in router, and adds tests covering matching and filter behavior.
Misc Cleanup & Tests
pkg/cluster/cluster.go, tests/server/apiv2/handlers/affinity_test.go
Removed a TODO comment block in HandleOverlaps; re-enabled previously skipped test TestAffinityForwardedHeader.

Sequence Diagram(s)

sequenceDiagram
    participant etcd as etcd
    participant Watcher as Affinity Watcher
    participant Manager as Affinity Manager
    participant Labeler as Region Labeler

    note over etcd,Watcher: etcd emits group / label-rule events

    etcd->>Watcher: Group/Rule Change Event
    activate Watcher

    alt Affinity Group Update
        Watcher->>Watcher: Unmarshal JSON → affinity.Group
        Watcher->>Manager: SyncGroupFromEtcd(groupID, group)
        activate Manager
        Manager->>Manager: Check labelRuleBuffer
        Manager->>Labeler: UpdateGroup (keyRanges)
        deactivate Manager
    else Affinity Group Delete
        Watcher->>Manager: SyncGroupDeleteFromEtcd(groupID)
        activate Manager
        Manager->>Manager: Remove group, clear buffers
        deactivate Manager
    end

    alt Label Rule Update
        Watcher->>Watcher: Filter "affinity_group/" prefix
        Watcher->>Watcher: Parse JSON → labeler.LabelRule
        Watcher->>Manager: SyncKeyRangesFromEtcd(ruleID, rule)
        activate Manager
        alt Group exists
            Manager->>Manager: Attach rule, update keyRanges
        else Group missing
            Manager->>Manager: Buffer rule in labelRuleBuffer
        end
        deactivate Manager
    else Label Rule Delete
        Watcher->>Manager: SyncKeyRangesDeleteFromEtcd(ruleID)
        activate Manager
        Manager->>Manager: Remove keyRanges, clear buffer
        deactivate Manager
    end

    deactivate Watcher
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🐰 I hopped through keys and watched the streams,
JSON crumbs and buffers stitched into dreams,
When rules jump ahead, the buffer holds tight,
Redirects now sparkle and routes fly right! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete. While it includes 'Issue Number: Ref #9764' and references unit tests, it lacks a detailed 'What is changed and how does it work?' section with substantive explanation of the changes. Fill in the 'What is changed and how does it work?' section with detailed explanation of the affinity redirect and scheduling watcher implementation and rationale.
Docstring Coverage ⚠️ Warning Docstring coverage is 75.68% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'mcs: add affinity redirect and scheduling watcher' clearly summarizes the main changes: adding affinity redirect functionality and a scheduling watcher in the mcs package.

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

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
pkg/mcs/scheduling/server/affinity/watcher_test.go (1)

230-243: Silently ignoring errors in concurrent test goroutines.

The error from putLabelRule is discarded with _ = putLabelRule(...). While the eventual consistency assertions will catch functional failures, silently ignoring errors can hide unexpected issues during test debugging.

Consider logging errors or collecting them for inspection:

💡 Suggested improvement
 		go func(idx int) {
 			defer wg.Done()
 			labelRule := makeTestLabelRule(groups[idx].ID,
 				"7480000000000000ff"+string(rune('0'+idx))+"000000000000000f8",
 				"7480000000000000ff"+string(rune('0'+idx))+"100000000000000f8",
 			)
-			_ = putLabelRule(ctx, client, labelRule)
+			if err := putLabelRule(ctx, client, labelRule); err != nil {
+				t.Logf("putLabelRule failed for group %s: %v", groups[idx].ID, err)
+			}
 		}(i)

Also applies to: 254-268


📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c869c8d and 05e8be8.

📒 Files selected for processing (2)
  • pkg/mcs/scheduling/server/affinity/watcher_test.go
  • pkg/schedule/affinity/manager.go
🧰 Additional context used
🪛 golangci-lint (2.5.0)
pkg/mcs/scheduling/server/affinity/watcher_test.go

[error] 36-36: : # github.com/tikv/pd/pkg/dashboard/uiserver
pkg/dashboard/uiserver/embedded_assets_rewriter.go:36:26: undefined: assets
pkg/dashboard/uiserver/embedded_assets_rewriter.go:37:13: undefined: vfsgen۰FS
pkg/dashboard/uiserver/embedded_assets_rewriter.go:39:15: undefined: vfsgen۰CompressedFileInfo
pkg/dashboard/uiserver/embedded_assets_rewriter.go:47:9: undefined: assets

(typecheck)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: chunks (7, Client Integration Test)
  • GitHub Check: chunks (2, Unit Test(2))
  • GitHub Check: chunks (9, Microservice Integration(!TSO))
  • GitHub Check: chunks (1, Unit Test(1))
  • GitHub Check: chunks (5, Tests(2))
  • GitHub Check: chunks (6, Tools Test)
  • GitHub Check: chunks (4, Tests(1))
  • GitHub Check: chunks (10, Microservice Integration(TSO))
  • GitHub Check: chunks (3, Unit Test(3))
  • GitHub Check: chunks (8, TSO Integration Test)
  • GitHub Check: chunks
  • GitHub Check: statics
  • GitHub Check: tso-function-test
🔇 Additional comments (8)
pkg/schedule/affinity/manager.go (4)

76-81: LGTM! Well-documented buffer field addition.

The labelRuleBuffer field is appropriately placed alongside keyRanges under metaMutex protection. The comment clearly explains its purpose for handling out-of-order etcd synchronization.


467-501: Buffer lookup logic is correct.

The fallback chain (regionLabeler → labelRuleBuffer) properly handles the case where label rules arrive via etcd watcher before the regionLabeler has synchronized. The buffer cleanup on both success and failure paths ensures no stale entries accumulate.


558-569: Early return optimization addresses previous feedback.

The logic correctly:

  1. Clears buffer and returns early when group doesn't exist and ranges are empty (lines 560-563)
  2. Buffers non-empty label rules for later attachment when group arrives
  3. Cleans up buffer after group exists

This addresses the previous review feedback about returning directly when !exist && len(gkr.KeyRanges) == 0.


523-525: Consistent buffer cleanup on deletions.

Both SyncGroupDeleteFromEtcd and SyncKeyRangesDeleteFromEtcd properly clean up labelRuleBuffer entries alongside keyRanges, preventing memory leaks from orphaned buffer entries.

Also applies to: 595-597

pkg/mcs/scheduling/server/affinity/watcher_test.go (4)

41-43: Good practice: goroutine leak detection.

Using goleak.VerifyTestMain ensures the tests catch any goroutine leaks from the watcher or manager, which is important for long-running components.


281-305: Excellent test coverage for the buffering behavior.

This test directly validates the new labelRuleBuffer functionality by writing a label rule before creating its corresponding group, then verifying the rule is properly attached after group creation. This aligns well with the manager changes.


307-327: Test cleanup function properly handles resources.

The cleanup function correctly:

  1. Cancels the context
  2. Closes etcd and client
  3. Removes the temporary directory

The os.RemoveAll(cfg.Dir) on line 314 before StartEtcd is intentional to ensure a clean slate.


116-162: Good boundary test for label rule prefix filtering.

Testing with affinity_group_v2/ prefix ensures the watcher correctly distinguishes between valid affinity rules (affinity_group/) and similar-looking but invalid prefixes. This prevents accidental processing of unrelated rules.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

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: 0

🧹 Nitpick comments (3)
pkg/utils/apiutil/serverapi/middleware_test.go (1)

57-78: Comprehensive edge-case coverage, consider adding a positive test.

The test suite thoroughly covers failure scenarios:

  • Forbidden header blocks redirect
  • Keyspace group disabled blocks redirect
  • Non-independent service blocks redirect
  • Filter function blocks redirect

Consider adding a test for the successful redirect path where all conditions are met and a valid primary address is returned.

✅ Optional: Add positive test case
+func TestMatchMicroserviceRedirectSuccess(t *testing.T) {
+	re := require.New(t)
+	req, err := http.NewRequest(http.MethodGet, "/pd/api/v1/foo", http.NoBody)
+	re.NoError(err)
+
+	matched, addr := MatchMicroserviceRedirect(
+		req,
+		[]RedirectRule{{
+			MatchPath:         "/pd/api/v1/foo",
+			TargetPath:        "/scheduling/api/v1/bar",
+			TargetServiceName: constant.SchedulingServiceName,
+			MatchMethods:      []string{http.MethodGet},
+		}},
+		true,
+		func(string) bool { return true },
+		func(_ context.Context, _ string) (string, bool) { return "http://scheduling:3379", true },
+	)
+
+	re.True(matched)
+	re.Equal("http://scheduling:3379", addr)
+	re.Equal("/scheduling/api/v1/bar", req.URL.Path)
+}

Also applies to: 80-100, 102-122, 124-147

pkg/mcs/scheduling/server/affinity/watcher_test.go (1)

237-242: Consider using fmt.Sprintf for clearer hex string generation.

The current approach using string(rune('0'+idx)) works but is less readable. Using fmt.Sprintf would make the intent clearer.

♻️ Suggested improvement
-		labelRule := makeTestLabelRule(groups[idx].ID,
-			"7480000000000000ff"+string(rune('0'+idx))+"000000000000000f8",
-			"7480000000000000ff"+string(rune('0'+idx))+"100000000000000f8",
-		)
+		labelRule := makeTestLabelRule(groups[idx].ID,
+			fmt.Sprintf("7480000000000000ff%d000000000000000f8", idx),
+			fmt.Sprintf("7480000000000000ff%d100000000000000f8", idx),
+		)
pkg/mcs/scheduling/server/affinity/watcher.go (1)

97-103: Consider validating the extracted group ID in deleteFn.

If TrimPrefix doesn't find the prefix, it returns the original key unchanged, which could lead to unexpected behavior. A validation check would be safer.

♻️ Suggested improvement
 	deleteFn := func(kv *mvccpb.KeyValue) error {
 		key := string(kv.Key)
 		log.Info("delete affinity group", zap.String("key", key))
-		groupID := strings.TrimPrefix(key, keypath.AffinityGroupsPrefix())
+		prefix := keypath.AffinityGroupsPrefix()
+		if !strings.HasPrefix(key, prefix) {
+			log.Warn("unexpected key format in delete event", zap.String("key", key))
+			return nil
+		}
+		groupID := strings.TrimPrefix(key, prefix)
 		w.affinityManager.SyncGroupDeleteFromEtcd(groupID)
 		return nil
 	}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09f1b5d and c869c8d.

📒 Files selected for processing (15)
  • pkg/cluster/cluster.go
  • pkg/mcs/scheduling/server/affinity/watcher.go
  • pkg/mcs/scheduling/server/affinity/watcher_test.go
  • pkg/mcs/scheduling/server/apis/v1/api.go
  • pkg/mcs/scheduling/server/rule/watcher.go
  • pkg/mcs/scheduling/server/server.go
  • pkg/schedule/affinity/manager.go
  • pkg/schedule/affinity/manager_test.go
  • pkg/schedule/labeler/labeler.go
  • pkg/schedule/labeler/rules.go
  • pkg/utils/apiutil/serverapi/middleware.go
  • pkg/utils/apiutil/serverapi/middleware_test.go
  • server/apiv2/middlewares/microservice_redirector.go
  • server/apiv2/router.go
  • tests/server/apiv2/handlers/affinity_test.go
💤 Files with no reviewable changes (2)
  • tests/server/apiv2/handlers/affinity_test.go
  • pkg/cluster/cluster.go
🧰 Additional context used
🧬 Code graph analysis (8)
pkg/schedule/labeler/labeler.go (1)
pkg/schedule/labeler/rules.go (1)
  • NewLabelRuleFromJSONWithoutCheck (103-109)
pkg/utils/apiutil/serverapi/middleware_test.go (2)
pkg/mcs/utils/constant/constant.go (2)
  • TSOServiceName (51-51)
  • SchedulingServiceName (55-55)
pkg/utils/apiutil/apiutil.go (1)
  • XForbiddenForwardToMicroserviceHeader (71-71)
pkg/mcs/scheduling/server/affinity/watcher.go (4)
pkg/utils/etcdutil/etcdutil.go (2)
  • LoopWatcher (346-387)
  • NewLoopWatcher (390-419)
pkg/utils/keypath/affinity.go (1)
  • AffinityGroupsPrefix (30-34)
pkg/schedule/labeler/rules.go (1)
  • NewLabelRuleFromJSON (91-100)
pkg/utils/keypath/rule.go (2)
  • RegionLabelPathPrefix (52-54)
  • RegionLabelKeyPath (42-44)
server/apiv2/router.go (1)
server/apiv2/middlewares/microservice_redirector.go (1)
  • AffinityMicroserviceRedirector (67-77)
pkg/utils/apiutil/serverapi/middleware.go (2)
pkg/mcs/utils/constant/constant.go (1)
  • SchedulingServiceName (55-55)
client/servicediscovery/tso_service_discovery.go (1)
  • Contains (772-779)
pkg/schedule/affinity/manager.go (3)
pkg/schedule/affinity/group.go (1)
  • GroupKeyRanges (308-311)
pkg/schedule/labeler/rules.go (1)
  • LabelRule (50-57)
pkg/utils/keyutil/keyrange.go (1)
  • KeyRanges (95-97)
pkg/mcs/scheduling/server/rule/watcher.go (1)
pkg/schedule/labeler/rules.go (1)
  • NewLabelRuleFromJSONWithoutCheck (103-109)
pkg/mcs/scheduling/server/affinity/watcher_test.go (4)
pkg/utils/keypath/rule.go (1)
  • RegionLabelKeyPath (42-44)
pkg/utils/keypath/affinity.go (1)
  • AffinityGroupPath (23-25)
pkg/mcs/scheduling/server/affinity/watcher.go (2)
  • Watcher (45-56)
  • NewWatcher (59-82)
pkg/schedule/affinity/txn.go (1)
  • LabelRuleIDPrefix (41-41)
🪛 golangci-lint (2.5.0)
pkg/utils/apiutil/serverapi/middleware_test.go

[error] 36-36: : # github.com/tikv/pd/pkg/dashboard/uiserver
pkg/dashboard/uiserver/embedded_assets_rewriter.go:36:26: undefined: assets
pkg/dashboard/uiserver/embedded_assets_rewriter.go:37:13: undefined: vfsgen۰FS
pkg/dashboard/uiserver/embedded_assets_rewriter.go:39:15: undefined: vfsgen۰CompressedFileInfo
pkg/dashboard/uiserver/embedded_assets_rewriter.go:47:9: undefined: assets

(typecheck)

pkg/mcs/scheduling/server/affinity/watcher.go

[error] 36-36: : # github.com/tikv/pd/pkg/dashboard/uiserver
pkg/dashboard/uiserver/embedded_assets_rewriter.go:36:26: undefined: assets
pkg/dashboard/uiserver/embedded_assets_rewriter.go:37:13: undefined: vfsgen۰FS
pkg/dashboard/uiserver/embedded_assets_rewriter.go:39:15: undefined: vfsgen۰CompressedFileInfo
pkg/dashboard/uiserver/embedded_assets_rewriter.go:47:9: undefined: assets

(typecheck)

server/apiv2/middlewares/microservice_redirector.go

[error] 36-36: : # github.com/tikv/pd/pkg/dashboard/uiserver
pkg/dashboard/uiserver/embedded_assets_rewriter.go:36:26: undefined: assets
pkg/dashboard/uiserver/embedded_assets_rewriter.go:37:13: undefined: vfsgen۰FS
pkg/dashboard/uiserver/embedded_assets_rewriter.go:39:15: undefined: vfsgen۰CompressedFileInfo
pkg/dashboard/uiserver/embedded_assets_rewriter.go:47:9: undefined: assets

(typecheck)

pkg/mcs/scheduling/server/affinity/watcher_test.go

[error] 36-36: : # github.com/tikv/pd/pkg/dashboard/uiserver
pkg/dashboard/uiserver/embedded_assets_rewriter.go:36:26: undefined: assets
pkg/dashboard/uiserver/embedded_assets_rewriter.go:37:13: undefined: vfsgen۰FS
pkg/dashboard/uiserver/embedded_assets_rewriter.go:39:15: undefined: vfsgen۰CompressedFileInfo
pkg/dashboard/uiserver/embedded_assets_rewriter.go:47:9: undefined: assets

(typecheck)

🔇 Additional comments (30)
server/apiv2/router.go (1)

62-62: LGTM! Affinity redirect middleware placement is correct.

The middleware is appropriately positioned after the general Redirector() and before RegisterAffinity(), ensuring affinity requests are intercepted and forwarded to the Scheduling service when needed.

pkg/utils/apiutil/serverapi/middleware_test.go (2)

30-32: LGTM! Proper test setup with leak detection.

The TestMain setup correctly configures goleak to detect goroutine leaks during tests.


34-55: LGTM! Correctly tests no-primary-address edge case.

The test validates that when a rule matches but no primary address is available, the function returns matched=true with an empty address, allowing the caller to return ErrRedirect as expected.

server/apiv2/middlewares/microservice_redirector.go (2)

32-64: LGTM! Solid middleware implementation with proper error handling.

The middleware correctly:

  • Validates redirect rules using MatchMicroserviceRedirect
  • Returns ErrRedirect when a rule matches but no primary address is available (line 50)
  • Handles URL parsing errors appropriately (line 57)
  • Marks forwarded requests with the XForwardedToMicroserviceHeader (line 60)
  • Uses reverse proxy for transparent forwarding (line 61)
  • Aborts middleware chain after redirect (line 62)

66-77: LGTM! Clean affinity-specific wrapper.

The function provides a focused redirect rule for affinity groups, forwarding GET requests from the PD API v2 path to the Scheduling service's affinity endpoint. The implementation correctly leverages the generic MicroserviceRedirector.

pkg/utils/apiutil/serverapi/middleware.go (3)

81-88: LGTM! Well-designed public API for redirect rules.

The RedirectRule struct provides a clean, extensible interface with:

  • Clear field names for path matching and rewriting
  • Method filtering capability
  • Optional Filter function for custom logic

This design enables flexible redirect configuration across the codebase.


128-188: LGTM! Robust redirect matching with proper path handling.

The function correctly implements:

  • Early validation of keyspace group and rules (lines 137-139)
  • Header-based opt-out mechanism (lines 140-142)
  • Service independence checks (lines 148-150)
  • Path and method matching (line 151)
  • Optional filter invocation (lines 154-156)
  • Primary address resolution with proper error handling (lines 159-164)
  • URL path parameter extraction and rewriting (lines 166-182)

The path handling logic appropriately uses RawPath when present to preserve URL encoding, and cleanly extracts parameters for path rewriting.


103-117: LGTM! Constructor updated to use public RedirectRule type.

The MicroserviceRedirectRule option builder correctly constructs the new RedirectRule struct while maintaining backward compatibility with the existing API.

pkg/schedule/labeler/labeler.go (1)

112-141: LGTM: Deferred validation pattern correctly implemented.

The change to NewLabelRuleFromJSONWithoutCheck followed by immediate checkAndAdjust() maintains existing validation behavior while enabling other code paths to defer validation when needed.

pkg/mcs/scheduling/server/rule/watcher.go (1)

219-226: LGTM: Validation properly deferred to SetLabelRuleLocked.

The rule is parsed without validation, then validated and persisted via SetLabelRuleLocked (which calls checkAndAdjust internally). This ensures rules are validated before use.

pkg/schedule/labeler/rules.go (1)

90-109: LGTM: Clean separation of parsing and validation.

The refactored NewLabelRuleFromJSON maintains backward compatibility by immediately validating, while the new NewLabelRuleFromJSONWithoutCheck enables deferred validation for use cases like buffering rules before groups exist. Function names clearly communicate the difference.

pkg/schedule/affinity/manager_test.go (1)

178-217: LGTM: Test correctly validates out-of-order synchronization.

The test verifies that label rules arriving before their corresponding groups are properly buffered and applied after group creation. The JSON roundtrip (lines 205-208) correctly simulates the etcd watcher path to ensure type consistency.

pkg/mcs/scheduling/server/server.go (2)

530-534: LGTM: Affinity watcher properly initialized.

The watcher is correctly initialized after cluster creation with proper dependency injection and error handling.


560-573: LGTM: Proper watcher cleanup with nil checks.

The affinity watcher cleanup follows the same defensive pattern as other watchers, preventing nil pointer dereferences during shutdown.

pkg/mcs/scheduling/server/apis/v1/api.go (5)

89-92: LGTM - Response type is well-defined.

The AffinityGroupsResponse struct properly encapsulates the affinity groups map with appropriate JSON tags. Using map[string]*affinity.GroupState aligns with how the data is consumed by the handler.


267-274: LGTM - Affinity router follows established patterns.

The router registration is consistent with other routers in the file, properly applying the service redirector middleware and using appropriate RESTful routes.


1617-1634: LGTM - Proper validation with appropriate error responses.

The helper function correctly validates server state, cluster availability, and affinity manager presence before proceeding. The use of AbortWithStatusJSON ensures the request is terminated after sending the error response.


1642-1655: LGTM - Handler correctly builds the response.

The handler properly retrieves all affinity group states and constructs a map keyed by group ID for the JSON response.


1665-1684: LGTM - Comprehensive error handling.

The handler properly maps domain errors to appropriate HTTP status codes (400 for invalid ID, 404 for not found, 500 for internal errors). The error type checking pattern using Equal() is consistent with the codebase conventions.

pkg/mcs/scheduling/server/affinity/watcher_test.go (2)

308-328: LGTM - Test setup and cleanup are properly implemented.

The prepare function correctly sets up an embedded etcd instance for testing, and the cleanup function properly releases resources in the correct order (cancel context, close etcd, close client, remove directory).


45-114: LGTM - Comprehensive lifecycle test.

This test thoroughly covers the complete lifecycle of affinity groups and label rules, including creation, updates, and deletion. The use of testutil.Eventually ensures proper handling of asynchronous etcd operations.

pkg/schedule/affinity/manager.go (5)

76-80: LGTM - Buffer fields are well-documented.

The new labelRuleBuffer field with its explanatory comment clearly describes its purpose for handling out-of-order synchronization between label rules and groups during etcd synchronization.


467-473: LGTM - Buffer fallback logic is correct.

The buffer handling correctly falls back to labelRuleBuffer when the region labeler hasn't synchronized yet. Since the labelRule value is read before deletion, the subsequent operations can proceed safely.


557-564: LGTM - Buffering logic handles out-of-order synchronization correctly.

The implementation correctly buffers non-empty label rules that arrive before their corresponding group is created. When the group exists, the buffer is properly cleared to avoid stale data.


522-524: LGTM - Proper cleanup of both caches on group deletion.

The deletion correctly cleans up both keyRanges and labelRuleBuffer entries, ensuring no orphaned data remains after a group is removed.


566-571: LGTM - Cache synchronization handles both add and remove cases.

The logic properly maintains the keyRanges cache by storing non-empty ranges and explicitly removing entries when ranges become empty, preventing stale cache entries.

pkg/mcs/scheduling/server/affinity/watcher.go (4)

58-82: LGTM - Constructor with proper cleanup on initialization failure.

The NewWatcher constructor correctly calls Close() to clean up resources if either watcher initialization fails, preventing resource leaks.


144-156: LGTM - Label watcher correctly filters to affinity rules.

The watcher efficiently filters etcd events by using the LabelRuleIDPrefix in the watch path, ensuring only affinity-related label rules are processed. This aligns with the test case TestOnlyProcessAffinityGroupLabelRules.


158-162: LGTM - Clean shutdown implementation.

The Close method properly cancels the context first and then waits for all goroutines to finish. Both cancel() and wg.Wait() are safe to call multiple times, making this method idempotent.


35-56: LGTM - Well-documented Watcher struct.

The struct documentation clearly explains the two watched paths, their key formats, and value types. This makes the code self-documenting and easier to maintain.

Signed-off-by: lhy1024 <admin@liudos.us>
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. needs-cherry-pick-release-8.5 Should cherry pick this PR to release-8.5 branch. 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.

2 participants