Skip to content

Stabilize predicates plugin execution order and rollback semantics.#5259

Merged
volcano-sh-bot merged 3 commits intovolcano-sh:masterfrom
wangyang0616:fix_msg_change_random
May 8, 2026
Merged

Stabilize predicates plugin execution order and rollback semantics.#5259
volcano-sh-bot merged 3 commits intovolcano-sh:masterfrom
wangyang0616:fix_msg_change_random

Conversation

@wangyang0616
Copy link
Copy Markdown
Member

What type of PR is this?

This change updates the predicates plugin to use explicit execution order instead of map iteration:

  • Add per-stage plugin order slices in PredicatesPlugin (Filter / StableFilter / PreFilter / Reserve / PreBind / Score).
  • Record plugin execution order during InitPlugin() registration via unified addXxxPlugin helpers.
  • Replace direct range map execution with ordered-slice iteration across plugin stages.
  • Update rollback paths (runUnReservePlugins, PreBindRollBack) to call Unreserve in reverse reserveOrder, ensuring symmetric reserve/rollback behavior.

What this PR does / why we need it:

Which issue(s) this PR fixes:

Fixes #5249

Special notes for your reviewer:

Does this PR introduce a user-facing change?

NONE

Signed-off-by: wangyang0616 <wangyang8126@gmail.com>
Copilot AI review requested due to automatic review settings April 28, 2026 16:00
@volcano-sh-bot volcano-sh-bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Apr 28, 2026
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces deterministic execution order for scheduler plugins by adding explicit order slices to the PredicatesPlugin struct and updating the plugin initialization and execution logic to use these slices. Feedback was provided regarding the new unit tests, which initialize plugin maps but fail to populate the corresponding order slices, potentially leading to incorrect test behavior.

Comment on lines +588 to +598
pp.StableFilterPlugins = map[string]k8sframework.FilterPlugin{
nodeunschedulable.Name: &fakeFilterPlugin{name: nodeunschedulable.Name, message: "stable-nodeunschedulable"},
nodeaffinity.Name: &fakeFilterPlugin{name: nodeaffinity.Name, message: "stable-nodeaffinity"},
tainttoleration.Name: &fakeFilterPlugin{name: tainttoleration.Name, message: "stable-tainttoleration"},
}
pp.FilterPlugins = map[string]k8sframework.FilterPlugin{
nodeunschedulable.Name: &fakeFilterPlugin{name: nodeunschedulable.Name, message: "stable-nodeunschedulable"},
nodeaffinity.Name: &fakeFilterPlugin{name: nodeaffinity.Name, message: "stable-nodeaffinity"},
nodeports.Name: &fakeFilterPlugin{name: nodeports.Name, message: "normal-nodeports"},
tainttoleration.Name: &fakeFilterPlugin{name: tainttoleration.Name, message: "stable-tainttoleration"},
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The test TestPredicateFailureReasonAggregationOrderStable initializes FilterPlugins and StableFilterPlugins maps directly, but it doesn't initialize the corresponding filterPluginOrder and stableFilterOrder slices. The Predicate function now iterates over these slices to ensure stable execution order. Without these slices being populated, the predicate plugins won't be executed, and the test will not behave as intended.

To fix this, you should also initialize the order slices to match the intended execution order you want to test.

	pp.StableFilterPlugins = map[string]k8sframework.FilterPlugin{
		nodeunschedulable.Name: &fakeFilterPlugin{name: nodeunschedulable.Name, message: "stable-nodeunschedulable"},
		nodeaffinity.Name:      &fakeFilterPlugin{name: nodeaffinity.Name, message: "stable-nodeaffinity"},
		tainttoleration.Name:   &fakeFilterPlugin{name: tainttoleration.Name, message: "stable-tainttoleration"},
	}
	pp.FilterPlugins = map[string]k8sframework.FilterPlugin{
		nodeunschedulable.Name: &fakeFilterPlugin{name: nodeunschedulable.Name, message: "stable-nodeunschedulable"},
		nodeaffinity.Name:      &fakeFilterPlugin{name: nodeaffinity.Name, message: "stable-nodeaffinity"},
		nodeports.Name:         &fakeFilterPlugin{name: nodeports.Name, message: "normal-nodeports"},
		tainttoleration.Name:   &fakeFilterPlugin{name: tainttoleration.Name, message: "stable-tainttoleration"},
	}
	pp.stableFilterOrder = []string{
		nodeunschedulable.Name,
		nodeaffinity.Name,
		tainttoleration.Name,
	}
	pp.filterPluginOrder = []string{
		nodeunschedulable.Name,
		nodeaffinity.Name,
		nodeports.Name,
		tainttoleration.Name,
	}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines +648 to +651
pp.ReservePlugins = map[string]k8sframework.ReservePlugin{
vbcap.Name: &fakeReservePlugin{name: vbcap.Name, calls: &calls},
dynamicresources.Name: &fakeReservePlugin{name: dynamicresources.Name, calls: &calls},
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Similar to the TestPredicateFailureReasonAggregationOrderStable test, TestReserveRollbackOrderStable initializes the ReservePlugins map but does not set the reserveOrder slice. The runReservePlugins and PreBindRollBack functions rely on this slice for ordered execution and rollback. Without it, no reserve or unreserve operations will be performed, and the test will fail its assertion.

Please initialize reserveOrder to ensure the test correctly verifies the reservation and rollback logic.

Suggested change
pp.ReservePlugins = map[string]k8sframework.ReservePlugin{
vbcap.Name: &fakeReservePlugin{name: vbcap.Name, calls: &calls},
dynamicresources.Name: &fakeReservePlugin{name: dynamicresources.Name, calls: &calls},
}
pp.ReservePlugins = map[string]k8sframework.ReservePlugin{
vbcap.Name: &fakeReservePlugin{name: vbcap.Name, calls: &calls},
dynamicresources.Name: &fakeReservePlugin{name: dynamicresources.Name, calls: &calls},
}
pp.reserveOrder = []string{vbcap.Name, dynamicresources.Name}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR makes predicates plugin execution deterministic by replacing Go map iteration with explicit, recorded per-stage plugin orders, and ensures rollback semantics match reserve semantics by unreserving in reverse order.

Changes:

  • Add per-stage order slices (Filter/StableFilter/PreFilter/Reserve/PreBind/Score) to PredicatesPlugin and populate them during InitPlugin().
  • Switch all plugin-stage executions to iterate in the recorded order rather than ranging over maps.
  • Update rollback paths to unreserve in reverse reserveOrder, aligning reserve/unreserve behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 9 comments.

File Description
pkg/scheduler/plugins/predicates/predicates.go Introduces explicit plugin ordering for deterministic execution and reverses unreserve order for symmetric rollback.
pkg/scheduler/plugins/predicates/predicates_test.go Adds tests intended to validate stable failure-reason aggregation order and reverse unreserve rollback order.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +592 to +598
}
pp.FilterPlugins = map[string]k8sframework.FilterPlugin{
nodeunschedulable.Name: &fakeFilterPlugin{name: nodeunschedulable.Name, message: "stable-nodeunschedulable"},
nodeaffinity.Name: &fakeFilterPlugin{name: nodeaffinity.Name, message: "stable-nodeaffinity"},
nodeports.Name: &fakeFilterPlugin{name: nodeports.Name, message: "normal-nodeports"},
tainttoleration.Name: &fakeFilterPlugin{name: tainttoleration.Name, message: "stable-tainttoleration"},
}
"normal-nodeports",
}
for i := 0; i < 20; i++ {
err := pp.Predicate(task, volcanoNode, schedframework.NewCycleState())
pp.ReservePlugins = map[string]k8sframework.ReservePlugin{
vbcap.Name: &fakeReservePlugin{name: vbcap.Name, calls: &calls},
dynamicresources.Name: &fakeReservePlugin{name: dynamicresources.Name, calls: &calls},
}
task := api.NewTaskInfo(pod)

event := &framework.Event{Task: task}
pp.runReservePlugins(&framework.Session{}, event)
"k8s.io/client-go/informers"
k8sfake "k8s.io/client-go/kubernetes/fake"
k8sframework "k8s.io/kube-scheduler/framework"
schedframework "k8s.io/kubernetes/pkg/scheduler/framework"
},
},
}
k8sNodeInfo := schedframework.NewNodeInfo()
"normal-nodeports",
}
for i := 0; i < 20; i++ {
err := pp.Predicate(task, volcanoNode, schedframework.NewCycleState())

bindCtx := &cache.BindContext{
TaskInfo: task,
Extensions: map[string]cache.BindContextExtension{pp.Name(): &BindContextExtension{State: schedframework.NewCycleState()}},
Comment on lines 689 to +694
// Run all Filter plugins (except those in StableFilterPlugins)
for name, plugin := range pp.FilterPlugins {
for _, name := range pp.filterPluginOrder {
plugin, exists := pp.FilterPlugins[name]
if !exists {
continue
}
Normalize the predicates plugin field names for external access, align PreFilter-related naming with Kubernetes plugin terminology, and centralize reverse unreserve iteration in a shared helper to keep rollback behavior consistent.

Signed-off-by: wangyang0616 <wangyang8126@gmail.com>
Signed-off-by: wangyang0616 <wangyang8126@gmail.com>
@JesseStutler JesseStutler requested a review from Copilot May 6, 2026 07:40
@JesseStutler
Copy link
Copy Markdown
Member

/cc
Will take a look this week, thanks

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@JesseStutler
Copy link
Copy Markdown
Member

/approve
/lgtm
Thanks!

@volcano-sh-bot volcano-sh-bot added the lgtm Indicates that a PR is ready to be merged. label May 8, 2026
@volcano-sh-bot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: JesseStutler

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

@volcano-sh-bot volcano-sh-bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label May 8, 2026
@JesseStutler
Copy link
Copy Markdown
Member

/cherrypick release-1.14

@volcano-sh-bot
Copy link
Copy Markdown
Contributor

@JesseStutler: once the present PR merges, I will cherry-pick it on top of release-1.14 in a new PR and assign it to you.

Details

In response to this:

/cherrypick release-1.14

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/test-infra repository.

@volcano-sh-bot volcano-sh-bot merged commit 27f49b2 into volcano-sh:master May 8, 2026
29 checks passed
@volcano-sh-bot
Copy link
Copy Markdown
Contributor

@JesseStutler: #5259 failed to apply on top of branch "release-1.14":

Applying: Stabilize predicates plugin execution order and rollback semantics.
Using index info to reconstruct a base tree...
M	pkg/scheduler/plugins/predicates/predicates.go
Falling back to patching base and 3-way merge...
Auto-merging pkg/scheduler/plugins/predicates/predicates.go
Applying: Unify exported predicates plugin field naming.
Using index info to reconstruct a base tree...
M	pkg/scheduler/plugins/predicates/predicates.go
Falling back to patching base and 3-way merge...
Auto-merging pkg/scheduler/plugins/predicates/predicates.go
CONFLICT (content): Merge conflict in pkg/scheduler/plugins/predicates/predicates.go
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0002 Unify exported predicates plugin field naming.
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

Details

In response to this:

/cherrypick release-1.14

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/test-infra repository.

@volcano-sh-bot
Copy link
Copy Markdown
Contributor

@JesseStutler: new issue created for failed cherrypick: #5281

Details

In response to this:

/cherrypick release-1.14

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/test-infra repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Non-deterministic plugin iteration order in predicates causes unstable scheduling failure messages and rollback semantics

4 participants