diff --git a/pkg/scheduler/backend/queue/active_queue.go b/pkg/scheduler/backend/queue/active_queue.go index fa1bca28368..0fdd0f0d450 100644 --- a/pkg/scheduler/backend/queue/active_queue.go +++ b/pkg/scheduler/backend/queue/active_queue.go @@ -323,14 +323,13 @@ func (aq *activeQueue) unlockedPop(logger klog.Logger) (*framework.QueuedPodInfo } aq.schedCycle++ - // Update metrics and reset the set of pending plugins for the next attempt. - // Note: We don't clear UnschedulablePlugins here because: - // 1. If the pod schedules successfully, we need UnschedulablePlugins for logging/debugging - // 2. If the pod fails to schedule again, UnschedulablePlugins will be overwritten anyway + // Update metrics for unschedulable plugins. + // Note: We don't clear UnschedulablePlugins and PendingPlugins here because: + // 1. If the pod schedules successfully, we need them for logging/debugging + // 2. If the pod fails to schedule, they will be cleared and repopulated in handleSchedulingFailure for plugin := range pInfo.UnschedulablePlugins.Union(pInfo.PendingPlugins) { metrics.UnschedulableReason(plugin, pInfo.Pod.Spec.SchedulerName).Dec() } - pInfo.PendingPlugins.Clear() pInfo.GatingPlugin = "" pInfo.GatingPluginEvents = nil diff --git a/pkg/scheduler/backend/queue/scheduling_queue_test.go b/pkg/scheduler/backend/queue/scheduling_queue_test.go index 585ab56ef8a..5fec77b1f23 100644 --- a/pkg/scheduler/backend/queue/scheduling_queue_test.go +++ b/pkg/scheduler/backend/queue/scheduling_queue_test.go @@ -649,7 +649,7 @@ func Test_InFlightPods(t *testing.T) { }, }, { - name: "popped pod preserves UnschedulablePlugins but clears PendingPlugins", + name: "popped pod preserves UnschedulablePlugins and PendingPlugins", isSchedulingQueueHintEnabled: true, initialPods: []*v1.Pod{pod1}, actions: []action{ @@ -670,9 +670,9 @@ func Test_InFlightPods(t *testing.T) { if !poppedPod.UnschedulablePlugins.Equal(sets.New("fooPlugin2")) { t.Errorf("QueuedPodInfo from Pop should preserve UnschedulablePlugins, expected fooPlugin2, got: %+v", poppedPod.UnschedulablePlugins) } - // PendingPlugins should still be cleared - if len(poppedPod.PendingPlugins) > 0 { - t.Errorf("QueuedPodInfo from Pop should have empty PendingPlugins, got instead: %+v", poppedPod.PendingPlugins) + // PendingPlugins are preserved after Pop() for logging + if !poppedPod.PendingPlugins.Equal(sets.New("fooPlugin1")) { + t.Errorf("QueuedPodInfo from Pop should preserve PendingPlugins, expected fooPlugin1, got: %+v", poppedPod.PendingPlugins) } }}, {callback: func(t *testing.T, q *PriorityQueue) { @@ -940,8 +940,10 @@ func TestPop(t *testing.T) { // Now check result of Pop. poppedPod = popPod(t, logger, q, pod) - if len(poppedPod.PendingPlugins) > 0 { - t.Errorf("QueuedPodInfo from Pop should have empty PendingPlugins, got instead: %+v", poppedPod) + // PendingPlugins are preserved after Pop() so they can be logged if scheduling + // succeeds, or cleared in handleSchedulingFailure() if it fails. + if len(poppedPod.PendingPlugins) != 1 || !poppedPod.PendingPlugins.Has("fooPlugin1") { + t.Errorf("QueuedPodInfo from Pop should preserve PendingPlugins, expected fooPlugin1, got instead: %+v", poppedPod) } }) } diff --git a/pkg/scheduler/schedule_one.go b/pkg/scheduler/schedule_one.go index 4908b711da7..1160f456b8f 100644 --- a/pkg/scheduler/schedule_one.go +++ b/pkg/scheduler/schedule_one.go @@ -342,7 +342,7 @@ func (sched *Scheduler) bindingCycle( } // Count pods scheduled after being flushed from unschedulablePods if assumedPodInfo.WasFlushedFromUnschedulable { - logger.Info("Pod scheduled after flush from unschedulablePods", "pod", klog.KObj(assumedPodInfo.Pod), "unschedulablePlugins", assumedPodInfo.UnschedulablePlugins) + logger.Info("Pod scheduled after flush from unschedulablePods", "pod", klog.KObj(assumedPodInfo.Pod), "unschedulablePlugins", assumedPodInfo.UnschedulablePlugins, "pendingPlugins", assumedPodInfo.PendingPlugins) metrics.PodScheduledAfterFlush.Inc() } // Run "postbind" plugins. @@ -1063,6 +1063,11 @@ func (sched *Scheduler) handleSchedulingFailure(ctx context.Context, fwk framewo err := status.AsError() errMsg := status.Message() + // Clear plugin sets to avoid stale data from previous scheduling attempts. + // They will be repopulated below for FitError cases. + podInfo.UnschedulablePlugins.Clear() + podInfo.PendingPlugins.Clear() + if err == ErrNoNodesAvailable { logger.V(2).Info("Unable to schedule pod; no nodes are registered to the cluster; waiting", "pod", klog.KObj(pod)) } else if fitError, ok := err.(*framework.FitError); ok { // Inject UnschedulablePlugins to PodInfo, which will be used later for moving Pods between queues efficiently.