Skip to content

Commit 8bd36c6

Browse files
authored
Merge pull request kubernetes#125197 from gabesaba/prefilter_perf
[scheduler] absent key in NodeToStatusMap implies UnschedulableAndUnresolvable
2 parents 548d50d + c8f0ea1 commit 8bd36c6

File tree

7 files changed

+49
-28
lines changed

7 files changed

+49
-28
lines changed

pkg/scheduler/framework/interface.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ type NodeScore struct {
4949
Score int64
5050
}
5151

52-
// NodeToStatusMap declares map from node name to its status.
52+
// NodeToStatusMap contains the statuses of the Nodes where the incoming Pod was not schedulable.
53+
// A PostFilter plugin that uses this map should interpret absent Nodes as UnschedulableAndUnresolvable.
5354
type NodeToStatusMap map[string]*Status
5455

5556
// NodePluginScores is a struct with node name and scores for that node.
@@ -448,6 +449,7 @@ type PostFilterPlugin interface {
448449
// If this scheduling cycle failed at PreFilter, all Nodes have the status from the rejector PreFilter plugin in NodeToStatusMap.
449450
// Note that the scheduling framework runs PostFilter plugins even when PreFilter returned UnschedulableAndUnresolvable.
450451
// In that case, NodeToStatusMap contains all Nodes with UnschedulableAndUnresolvable.
452+
// If there is no entry in the NodeToStatus map, its implicit status is UnschedulableAndUnresolvable.
451453
//
452454
// Also, ignoring Nodes with UnschedulableAndUnresolvable is the responsibility of each PostFilter plugin,
453455
// meaning NodeToStatusMap obviously could have Nodes with UnschedulableAndUnresolvable

pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,8 @@ func TestPostFilter(t *testing.T) {
289289
st.MakeNode().Name("node4").Capacity(nodeRes).Obj(),
290290
},
291291
filteredNodesStatuses: framework.NodeToStatusMap{
292-
"node3": framework.NewStatus(framework.UnschedulableAndUnresolvable),
292+
"node1": framework.NewStatus(framework.Unschedulable),
293+
"node2": framework.NewStatus(framework.Unschedulable),
293294
"node4": framework.NewStatus(framework.UnschedulableAndUnresolvable),
294295
},
295296
wantResult: framework.NewPostFilterResultWithNominatedNode(""),
@@ -1776,7 +1777,15 @@ func TestPreempt(t *testing.T) {
17761777
State: state,
17771778
Interface: &pl,
17781779
}
1779-
res, status := pe.Preempt(ctx, test.pod, make(framework.NodeToStatusMap))
1780+
1781+
// so that these nodes are eligible for preemption, we set their status
1782+
// to Unschedulable.
1783+
nodeToStatusMap := make(framework.NodeToStatusMap, len(nodes))
1784+
for _, n := range nodes {
1785+
nodeToStatusMap[n.Name] = framework.NewStatus(framework.Unschedulable)
1786+
}
1787+
1788+
res, status := pe.Preempt(ctx, test.pod, nodeToStatusMap)
17801789
if !status.IsSuccess() && !status.IsRejected() {
17811790
t.Errorf("unexpected error in preemption: %v", status.AsError())
17821791
}

pkg/scheduler/framework/preemption/preemption.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -415,15 +415,18 @@ func (ev *Evaluator) prepareCandidate(ctx context.Context, c Candidate, pod *v1.
415415
func nodesWherePreemptionMightHelp(nodes []*framework.NodeInfo, m framework.NodeToStatusMap) ([]*framework.NodeInfo, framework.NodeToStatusMap) {
416416
var potentialNodes []*framework.NodeInfo
417417
nodeStatuses := make(framework.NodeToStatusMap)
418+
unresolvableStatus := framework.NewStatus(framework.UnschedulableAndUnresolvable, "Preemption is not helpful for scheduling")
418419
for _, node := range nodes {
419-
name := node.Node().Name
420-
// We rely on the status by each plugin - 'Unschedulable' or 'UnschedulableAndUnresolvable'
421-
// to determine whether preemption may help or not on the node.
422-
if m[name].Code() == framework.UnschedulableAndUnresolvable {
423-
nodeStatuses[node.Node().Name] = framework.NewStatus(framework.UnschedulableAndUnresolvable, "Preemption is not helpful for scheduling")
424-
continue
420+
nodeName := node.Node().Name
421+
// We only attempt preemption on nodes with status 'Unschedulable'. For
422+
// diagnostic purposes, we propagate UnschedulableAndUnresolvable if either
423+
// implied by absence in map or explicitly set.
424+
status, ok := m[nodeName]
425+
if status.Code() == framework.Unschedulable {
426+
potentialNodes = append(potentialNodes, node)
427+
} else if !ok || status.Code() == framework.UnschedulableAndUnresolvable {
428+
nodeStatuses[nodeName] = unresolvableStatus
425429
}
426-
potentialNodes = append(potentialNodes, node)
427430
}
428431
return potentialNodes, nodeStatuses
429432
}

pkg/scheduler/framework/preemption/preemption_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) {
147147
"node1": framework.NewStatus(framework.Unschedulable, interpodaffinity.ErrReasonAntiAffinityRulesNotMatch),
148148
"node2": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodename.ErrReason),
149149
"node3": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodeunschedulable.ErrReasonUnschedulable),
150+
"node4": framework.NewStatus(framework.Unschedulable, "Unschedulable"),
150151
},
151152
expected: sets.New("node1", "node4"),
152153
},
@@ -155,6 +156,8 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) {
155156
nodesStatuses: framework.NodeToStatusMap{
156157
"node1": framework.NewStatus(framework.UnschedulableAndUnresolvable, interpodaffinity.ErrReasonAffinityRulesNotMatch),
157158
"node2": framework.NewStatus(framework.Unschedulable, interpodaffinity.ErrReasonAntiAffinityRulesNotMatch),
159+
"node3": framework.NewStatus(framework.Unschedulable, "Unschedulable"),
160+
"node4": framework.NewStatus(framework.Unschedulable, "Unschedulable"),
158161
},
159162
expected: sets.New("node2", "node3", "node4"),
160163
},
@@ -163,13 +166,18 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) {
163166
nodesStatuses: framework.NodeToStatusMap{
164167
"node1": framework.NewStatus(framework.UnschedulableAndUnresolvable, volumerestrictions.ErrReasonDiskConflict),
165168
"node2": framework.NewStatus(framework.Unschedulable, fmt.Sprintf("Insufficient %v", v1.ResourceMemory)),
169+
"node3": framework.NewStatus(framework.Unschedulable, "Unschedulable"),
170+
"node4": framework.NewStatus(framework.Unschedulable, "Unschedulable"),
166171
},
167172
expected: sets.New("node2", "node3", "node4"),
168173
},
169174
{
170175
name: "Node condition errors should be considered unresolvable",
171176
nodesStatuses: framework.NodeToStatusMap{
172177
"node1": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodeunschedulable.ErrReasonUnknownCondition),
178+
"node2": framework.NewStatus(framework.Unschedulable, "Unschedulable"),
179+
"node3": framework.NewStatus(framework.Unschedulable, "Unschedulable"),
180+
"node4": framework.NewStatus(framework.Unschedulable, "Unschedulable"),
173181
},
174182
expected: sets.New("node2", "node3", "node4"),
175183
},
@@ -179,6 +187,7 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) {
179187
"node1": framework.NewStatus(framework.UnschedulableAndUnresolvable, volumezone.ErrReasonConflict),
180188
"node2": framework.NewStatus(framework.UnschedulableAndUnresolvable, string(volumebinding.ErrReasonNodeConflict)),
181189
"node3": framework.NewStatus(framework.UnschedulableAndUnresolvable, string(volumebinding.ErrReasonBindConflict)),
190+
"node4": framework.NewStatus(framework.Unschedulable, "Unschedulable"),
182191
},
183192
expected: sets.New("node4"),
184193
},
@@ -188,12 +197,14 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) {
188197
"node1": framework.NewStatus(framework.Unschedulable, podtopologyspread.ErrReasonConstraintsNotMatch),
189198
"node2": framework.NewStatus(framework.UnschedulableAndUnresolvable, nodename.ErrReason),
190199
"node3": framework.NewStatus(framework.Unschedulable, podtopologyspread.ErrReasonConstraintsNotMatch),
200+
"node4": framework.NewStatus(framework.Unschedulable, "Unschedulable"),
191201
},
192202
expected: sets.New("node1", "node3", "node4"),
193203
},
194204
{
195205
name: "UnschedulableAndUnresolvable status should be skipped but Unschedulable should be tried",
196206
nodesStatuses: framework.NodeToStatusMap{
207+
"node1": framework.NewStatus(framework.Unschedulable, ""),
197208
"node2": framework.NewStatus(framework.UnschedulableAndUnresolvable, ""),
198209
"node3": framework.NewStatus(framework.Unschedulable, ""),
199210
"node4": framework.NewStatus(framework.UnschedulableAndUnresolvable, ""),
@@ -203,6 +214,7 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) {
203214
{
204215
name: "ErrReasonNodeLabelNotMatch should not be tried as it indicates that the pod is unschedulable due to node doesn't have the required label",
205216
nodesStatuses: framework.NodeToStatusMap{
217+
"node1": framework.NewStatus(framework.Unschedulable, ""),
206218
"node2": framework.NewStatus(framework.UnschedulableAndUnresolvable, podtopologyspread.ErrReasonNodeLabelNotMatch),
207219
"node3": framework.NewStatus(framework.Unschedulable, ""),
208220
"node4": framework.NewStatus(framework.UnschedulableAndUnresolvable, ""),

pkg/scheduler/framework/types.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,9 +325,11 @@ const ExtenderName = "Extender"
325325

326326
// Diagnosis records the details to diagnose a scheduling failure.
327327
type Diagnosis struct {
328-
// NodeToStatusMap records the status of each node
328+
// NodeToStatusMap records the status of each retriable node (status Unschedulable)
329329
// if they're rejected in PreFilter (via PreFilterResult) or Filter plugins.
330330
// Nodes that pass PreFilter/Filter plugins are not included in this map.
331+
// While this map may contain UnschedulableAndUnresolvable statuses, the absence of
332+
// a node should be interpreted as UnschedulableAndUnresolvable.
331333
NodeToStatusMap NodeToStatusMap
332334
// UnschedulablePlugins are plugins that returns Unschedulable or UnschedulableAndUnresolvable.
333335
UnschedulablePlugins sets.Set[string]

pkg/scheduler/schedule_one.go

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -441,14 +441,13 @@ func (sched *Scheduler) schedulePod(ctx context.Context, fwk framework.Framework
441441
// filter plugins and filter extenders.
442442
func (sched *Scheduler) findNodesThatFitPod(ctx context.Context, fwk framework.Framework, state *framework.CycleState, pod *v1.Pod) ([]*framework.NodeInfo, framework.Diagnosis, error) {
443443
logger := klog.FromContext(ctx)
444+
diagnosis := framework.Diagnosis{
445+
NodeToStatusMap: make(framework.NodeToStatusMap),
446+
}
444447

445448
allNodes, err := sched.nodeInfoSnapshot.NodeInfos().List()
446449
if err != nil {
447-
return nil, framework.Diagnosis{}, err
448-
}
449-
450-
diagnosis := framework.Diagnosis{
451-
NodeToStatusMap: make(framework.NodeToStatusMap, len(allNodes)),
450+
return nil, diagnosis, err
452451
}
453452
// Run "prefilter" plugins.
454453
preRes, s := fwk.RunPreFilterPlugins(ctx, state, pod)
@@ -486,14 +485,12 @@ func (sched *Scheduler) findNodesThatFitPod(ctx context.Context, fwk framework.F
486485
nodes := allNodes
487486
if !preRes.AllNodes() {
488487
nodes = make([]*framework.NodeInfo, 0, len(preRes.NodeNames))
489-
for _, n := range allNodes {
490-
if !preRes.NodeNames.Has(n.Node().Name) {
491-
// We consider Nodes that are filtered out by PreFilterResult as rejected via UnschedulableAndUnresolvable.
492-
// We have to record them in NodeToStatusMap so that they won't be considered as candidates in the preemption.
493-
diagnosis.NodeToStatusMap[n.Node().Name] = framework.NewStatus(framework.UnschedulableAndUnresolvable, "node is filtered out by the prefilter result")
494-
continue
488+
for nodeName := range preRes.NodeNames {
489+
// PreRes may return nodeName(s) which do not exist; we verify
490+
// node exists in the Snapshot.
491+
if nodeInfo, err := sched.nodeInfoSnapshot.Get(nodeName); err == nil {
492+
nodes = append(nodes, nodeInfo)
495493
}
496-
nodes = append(nodes, n)
497494
}
498495
}
499496
feasibleNodes, err := sched.findNodesThatPassFilters(ctx, fwk, state, pod, &diagnosis, nodes)

pkg/scheduler/schedule_one_test.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,7 +2344,6 @@ func TestSchedulerSchedulePod(t *testing.T) {
23442344
NumAllNodes: 2,
23452345
Diagnosis: framework.Diagnosis{
23462346
NodeToStatusMap: framework.NodeToStatusMap{
2347-
"node1": framework.NewStatus(framework.UnschedulableAndUnresolvable, "node is filtered out by the prefilter result"),
23482347
"node2": framework.NewStatus(framework.Unschedulable, "injecting failure for pod test-prefilter").WithPlugin("FakeFilter"),
23492348
},
23502349
UnschedulablePlugins: sets.New("FakeFilter"),
@@ -2453,10 +2452,7 @@ func TestSchedulerSchedulePod(t *testing.T) {
24532452
Pod: st.MakePod().Name("test-prefilter").UID("test-prefilter").Obj(),
24542453
NumAllNodes: 2,
24552454
Diagnosis: framework.Diagnosis{
2456-
NodeToStatusMap: framework.NodeToStatusMap{
2457-
"1": framework.NewStatus(framework.UnschedulableAndUnresolvable, "node is filtered out by the prefilter result"),
2458-
"2": framework.NewStatus(framework.UnschedulableAndUnresolvable, "node is filtered out by the prefilter result"),
2459-
},
2455+
NodeToStatusMap: framework.NodeToStatusMap{},
24602456
},
24612457
},
24622458
},

0 commit comments

Comments
 (0)