2017-10-23 22:26:59 -04:00
/ *
Copyright 2017 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
// This file contains structures that implement scheduling queue types.
2022-03-24 05:38:49 -04:00
// Scheduling queues hold pods waiting to be scheduled. This file implements a
// priority queue which has two sub queues and a additional data structure,
// namely: activeQ, backoffQ and unschedulablePods.
// - activeQ holds pods that are being considered for scheduling.
// - backoffQ holds pods that moved from unschedulablePods and will move to
// activeQ when their backoff periods complete.
// - unschedulablePods holds pods that were already attempted for scheduling and
// are currently determined to be unschedulable.
2017-10-23 22:26:59 -04:00
2018-09-21 19:18:48 -04:00
package queue
2017-10-23 22:26:59 -04:00
import (
2023-07-17 18:53:07 -04:00
"container/list"
2022-11-07 17:02:22 -05:00
"context"
2017-10-24 14:14:29 -04:00
"fmt"
2023-03-01 21:57:50 -05:00
"math/rand"
2018-03-19 06:25:12 -04:00
"reflect"
2017-10-24 14:14:29 -04:00
"sync"
2018-07-24 16:46:40 -04:00
"time"
2017-10-24 14:14:29 -04:00
2021-08-24 04:22:10 -04:00
v1 "k8s.io/api/core/v1"
2021-04-09 03:31:32 -04:00
"k8s.io/apimachinery/pkg/types"
2021-01-29 01:29:10 -05:00
"k8s.io/apimachinery/pkg/util/sets"
2018-07-24 16:46:40 -04:00
"k8s.io/apimachinery/pkg/util/wait"
2022-11-04 16:53:49 -04:00
utilfeature "k8s.io/apiserver/pkg/util/feature"
2021-03-04 07:30:24 -05:00
"k8s.io/client-go/informers"
listersv1 "k8s.io/client-go/listers/core/v1"
2017-10-23 22:26:59 -04:00
"k8s.io/client-go/tools/cache"
2021-03-04 07:30:24 -05:00
"k8s.io/klog/v2"
2022-11-04 16:53:49 -04:00
"k8s.io/kubernetes/pkg/features"
2020-10-09 10:41:44 -04:00
"k8s.io/kubernetes/pkg/scheduler/framework"
2021-03-04 07:30:24 -05:00
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/interpodaffinity"
2019-09-27 04:57:27 -04:00
"k8s.io/kubernetes/pkg/scheduler/internal/heap"
2019-03-19 02:43:43 -04:00
"k8s.io/kubernetes/pkg/scheduler/metrics"
2018-01-03 21:12:18 -05:00
"k8s.io/kubernetes/pkg/scheduler/util"
2022-06-24 12:24:34 -04:00
"k8s.io/utils/clock"
2017-10-23 22:26:59 -04:00
)
2019-08-11 20:26:32 -04:00
const (
2022-03-24 05:38:49 -04:00
// DefaultPodMaxInUnschedulablePodsDuration is the default value for the maximum
// time a pod can stay in unschedulablePods. If a pod stays in unschedulablePods
// for longer than this value, the pod will be moved from unschedulablePods to
2022-03-17 03:37:34 -04:00
// backoffQ or activeQ. If this value is empty, the default value (5min)
2022-02-15 22:05:52 -05:00
// will be used.
2022-03-24 05:38:49 -04:00
DefaultPodMaxInUnschedulablePodsDuration time . Duration = 5 * time . Minute
2022-08-16 20:47:38 -04:00
// Scheduling queue names
2023-06-05 12:02:06 -04:00
activeQ = "Active"
backoffQ = "Backoff"
2022-08-16 20:47:38 -04:00
unschedulablePods = "Unschedulable"
2022-11-07 17:02:22 -05:00
preEnqueue = "PreEnqueue"
2018-09-14 19:59:54 -04:00
)
2019-08-11 20:26:32 -04:00
const (
// DefaultPodInitialBackoffDuration is the default value for the initial backoff duration
// for unschedulable pods. To change the default podInitialBackoffDurationSeconds used by the
// scheduler, update the ComponentConfig value in defaults.go
DefaultPodInitialBackoffDuration time . Duration = 1 * time . Second
// DefaultPodMaxBackoffDuration is the default value for the max backoff duration
// for unschedulable pods. To change the default podMaxBackoffDurationSeconds used by the
// scheduler, update the ComponentConfig value in defaults.go
DefaultPodMaxBackoffDuration time . Duration = 10 * time . Second
)
2019-01-15 23:08:19 -05:00
2021-03-11 15:31:33 -05:00
// PreEnqueueCheck is a function type. It's used to build functions that
// run against a Pod and the caller can choose to enqueue or skip the Pod
// by the checking result.
type PreEnqueueCheck func ( pod * v1 . Pod ) bool
2017-10-23 22:26:59 -04:00
// SchedulingQueue is an interface for a queue to store pods waiting to be scheduled.
// The interface follows a pattern similar to cache.FIFO and cache.Heap and
// makes it easy to use those data structures as a SchedulingQueue.
type SchedulingQueue interface {
2020-05-18 13:29:08 -04:00
framework . PodNominator
2023-03-22 07:48:04 -04:00
Add ( logger klog . Logger , pod * v1 . Pod ) error
2022-03-24 05:38:49 -04:00
// Activate moves the given pods to activeQ iff they're in unschedulablePods or backoffQ.
2021-06-30 12:57:53 -04:00
// The passed-in pods are originally compiled from plugins that want to activate Pods,
// by injecting the pods through a reserved CycleState struct (PodsToActivate).
2023-03-22 07:48:04 -04:00
Activate ( logger klog . Logger , pods map [ string ] * v1 . Pod )
2019-01-25 05:39:53 -05:00
// AddUnschedulableIfNotPresent adds an unschedulable pod back to scheduling queue.
// The podSchedulingCycle represents the current scheduling cycle number which can be
// returned by calling SchedulingCycle().
2023-03-22 07:48:04 -04:00
AddUnschedulableIfNotPresent ( logger klog . Logger , pod * framework . QueuedPodInfo , podSchedulingCycle int64 ) error
2019-01-25 05:39:53 -05:00
// SchedulingCycle returns the current number of scheduling cycle which is
// cached by scheduling queue. Normally, incrementing this number whenever
// a pod is popped (e.g. called Pop()) is enough.
SchedulingCycle ( ) int64
2018-09-14 19:59:54 -04:00
// Pop removes the head of the queue and returns it. It blocks if the
// queue is empty and waits until a new item is added to the queue.
2023-09-28 22:59:22 -04:00
Pop ( logger klog . Logger ) ( * framework . QueuedPodInfo , error )
2023-07-17 18:53:07 -04:00
// Done must be called for pod returned by Pop. This allows the queue to
// keep track of which pods are currently being processed.
Done ( types . UID )
2023-03-22 07:48:04 -04:00
Update ( logger klog . Logger , oldPod , newPod * v1 . Pod ) error
2017-10-24 14:14:29 -04:00
Delete ( pod * v1 . Pod ) error
2023-06-08 00:54:30 -04:00
// TODO(sanposhiho): move all PreEnqueueCkeck to Requeue and delete it from this parameter eventually.
// Some PreEnqueueCheck include event filtering logic based on some in-tree plugins
// and it affect badly to other plugins.
// See https://github.com/kubernetes/kubernetes/issues/110175
MoveAllToActiveOrBackoffQueue ( logger klog . Logger , event framework . ClusterEvent , oldObj , newObj interface { } , preCheck PreEnqueueCheck )
2023-03-22 07:48:04 -04:00
AssignedPodAdded ( logger klog . Logger , pod * v1 . Pod )
2023-06-08 00:54:30 -04:00
AssignedPodUpdated ( logger klog . Logger , oldPod , newPod * v1 . Pod )
2022-08-05 21:30:09 -04:00
PendingPods ( ) ( [ ] * v1 . Pod , string )
2018-09-14 19:59:54 -04:00
// Close closes the SchedulingQueue so that the goroutine which is
// waiting to pop items can exit gracefully.
Close ( )
2019-12-02 14:35:09 -05:00
// Run starts the goroutines managing the queue.
2023-03-22 07:48:04 -04:00
Run ( logger klog . Logger )
2017-10-24 14:14:29 -04:00
}
2019-04-08 11:41:10 -04:00
// NewSchedulingQueue initializes a priority queue as a new scheduling queue.
2021-03-04 07:30:24 -05:00
func NewSchedulingQueue (
lessFn framework . LessFunc ,
informerFactory informers . SharedInformerFactory ,
opts ... Option ) SchedulingQueue {
return NewPriorityQueue ( lessFn , informerFactory , opts ... )
2017-10-24 14:14:29 -04:00
}
2018-02-08 21:19:31 -05:00
// NominatedNodeName returns nominated node name of a Pod.
func NominatedNodeName ( pod * v1 . Pod ) string {
return pod . Status . NominatedNodeName
}
2019-04-08 11:41:10 -04:00
// PriorityQueue implements a scheduling queue.
2017-10-24 14:14:29 -04:00
// The head of PriorityQueue is the highest priority pending pod. This structure
2022-03-24 05:38:49 -04:00
// has two sub queues and a additional data structure, namely: activeQ,
// backoffQ and unschedulablePods.
2022-07-19 20:54:13 -04:00
// - activeQ holds pods that are being considered for scheduling.
// - backoffQ holds pods that moved from unschedulablePods and will move to
// activeQ when their backoff periods complete.
// - unschedulablePods holds pods that were already attempted for scheduling and
// are currently determined to be unschedulable.
2017-10-24 14:14:29 -04:00
type PriorityQueue struct {
2023-03-08 16:18:36 -05:00
* nominator
2020-05-18 13:29:08 -04:00
2019-12-02 14:35:09 -05:00
stop chan struct { }
2022-06-24 12:24:34 -04:00
clock clock . Clock
2020-02-13 19:31:07 -05:00
// pod initial backoff duration.
podInitialBackoffDuration time . Duration
// pod maximum backoff duration.
podMaxBackoffDuration time . Duration
2022-03-24 05:38:49 -04:00
// the maximum time a pod can stay in the unschedulablePods.
podMaxInUnschedulablePodsDuration time . Duration
2018-07-24 16:46:40 -04:00
2017-10-24 14:14:29 -04:00
cond sync . Cond
2023-07-17 18:53:07 -04:00
// inFlightPods holds the UID of all pods which have been popped out for which Done
// hasn't been called yet - in other words, all pods that are currently being
// processed (being scheduled, in permit, or in the binding cycle).
2023-09-04 09:48:45 -04:00
//
// The values in the map are the entry of each pod in the inFlightEvents list.
// The value of that entry is the *v1.Pod at the time that scheduling of that
// pod started, which can be useful for logging or debugging.
inFlightPods map [ types . UID ] * list . Element
// inFlightEvents holds the events received by the scheduling queue
// (entry value is clusterEvent) together with in-flight pods (entry
// value is *v1.Pod). Entries get added at the end while the mutex is
// locked, so they get serialized.
//
// The pod entries are added in Pop and used to track which events
// occurred after the pod scheduling attempt for that pod started.
// They get removed when the scheduling attempt is done, at which
// point all events that occurred in the meantime are processed.
//
// After removal of a pod, events at the start of the list are no
// longer needed because all of the other in-flight pods started
// later. Those events can be removed.
inFlightEvents * list . List
2023-07-17 18:53:07 -04:00
2017-10-24 14:14:29 -04:00
// activeQ is heap structure that scheduler actively looks at to find pods to
// schedule. Head of heap is the highest priority pod.
2019-09-27 04:57:27 -04:00
activeQ * heap . Heap
2018-07-24 16:46:40 -04:00
// podBackoffQ is a heap ordered by backoff expiry. Pods which have completed backoff
// are popped from this heap before the scheduler looks at activeQ
2019-09-27 04:57:27 -04:00
podBackoffQ * heap . Heap
2022-03-24 05:38:49 -04:00
// unschedulablePods holds pods that have been tried and determined unschedulable.
unschedulablePods * UnschedulablePods
2019-01-25 05:39:53 -05:00
// schedulingCycle represents sequence number of scheduling cycle and is incremented
// when a pod is popped.
schedulingCycle int64
// moveRequestCycle caches the sequence number of scheduling cycle when we
2021-01-09 12:59:33 -05:00
// received a move request. Unschedulable pods in and before this scheduling
2019-01-25 05:39:53 -05:00
// cycle will be put back to activeQueue if we were trying to schedule them
// when we received move request.
2023-07-17 18:53:07 -04:00
// TODO: this will be removed after SchedulingQueueHint goes to stable and the feature gate is removed.
2019-01-25 05:39:53 -05:00
moveRequestCycle int64
2018-09-14 19:59:54 -04:00
2022-11-07 17:02:22 -05:00
// preEnqueuePluginMap is keyed with profile name, valued with registered preEnqueue plugins.
preEnqueuePluginMap map [ string ] [ ] framework . PreEnqueuePlugin
2023-06-08 00:54:30 -04:00
// queueingHintMap is keyed with profile name, valued with registered queueing hint functions.
queueingHintMap QueueingHintMapPerProfile
2021-01-29 01:29:10 -05:00
2018-09-14 19:59:54 -04:00
// closed indicates that the queue is closed.
// It is mainly used to let Pop() exit its control loop while waiting for an item.
closed bool
2021-03-04 07:30:24 -05:00
nsLister listersv1 . NamespaceLister
2023-03-01 21:57:50 -05:00
metricsRecorder metrics . MetricAsyncRecorder
// pluginMetricsSamplePercent is the percentage of plugin metrics to be sampled.
pluginMetricsSamplePercent int
2023-07-17 18:53:07 -04:00
// isSchedulingQueueHintEnabled indicates whether the feature gate for the scheduling queue is enabled.
isSchedulingQueueHintEnabled bool
2017-10-24 14:14:29 -04:00
}
2023-06-08 00:54:30 -04:00
// QueueingHintFunction is the wrapper of QueueingHintFn that has PluginName.
type QueueingHintFunction struct {
PluginName string
QueueingHintFn framework . QueueingHintFn
}
2023-07-17 18:53:07 -04:00
// clusterEvent has the event and involved objects.
type clusterEvent struct {
event framework . ClusterEvent
// oldObj is the object that involved this event.
oldObj interface { }
// newObj is the object that involved this event.
newObj interface { }
}
2019-08-11 20:26:32 -04:00
type priorityQueueOptions struct {
2022-06-24 12:24:34 -04:00
clock clock . Clock
2022-03-24 05:38:49 -04:00
podInitialBackoffDuration time . Duration
podMaxBackoffDuration time . Duration
podMaxInUnschedulablePodsDuration time . Duration
2023-03-08 16:18:36 -05:00
podLister listersv1 . PodLister
2023-03-01 21:57:50 -05:00
metricsRecorder metrics . MetricAsyncRecorder
pluginMetricsSamplePercent int
2022-11-07 17:02:22 -05:00
preEnqueuePluginMap map [ string ] [ ] framework . PreEnqueuePlugin
2023-06-08 00:54:30 -04:00
queueingHintMap QueueingHintMapPerProfile
2019-08-11 20:26:32 -04:00
}
// Option configures a PriorityQueue
type Option func ( * priorityQueueOptions )
2022-06-24 12:24:34 -04:00
// WithClock sets clock for PriorityQueue, the default clock is clock.RealClock.
func WithClock ( clock clock . Clock ) Option {
2019-08-11 20:26:32 -04:00
return func ( o * priorityQueueOptions ) {
o . clock = clock
}
}
2020-05-18 13:29:08 -04:00
// WithPodInitialBackoffDuration sets pod initial backoff duration for PriorityQueue.
2019-08-11 20:26:32 -04:00
func WithPodInitialBackoffDuration ( duration time . Duration ) Option {
return func ( o * priorityQueueOptions ) {
o . podInitialBackoffDuration = duration
}
}
2020-05-18 13:29:08 -04:00
// WithPodMaxBackoffDuration sets pod max backoff duration for PriorityQueue.
2019-08-11 20:26:32 -04:00
func WithPodMaxBackoffDuration ( duration time . Duration ) Option {
return func ( o * priorityQueueOptions ) {
o . podMaxBackoffDuration = duration
}
}
2023-03-08 16:18:36 -05:00
// WithPodLister sets pod lister for PriorityQueue.
func WithPodLister ( pl listersv1 . PodLister ) Option {
2020-05-18 13:29:08 -04:00
return func ( o * priorityQueueOptions ) {
2023-03-08 16:18:36 -05:00
o . podLister = pl
2020-05-18 13:29:08 -04:00
}
}
2023-06-08 00:54:30 -04:00
// WithPodMaxInUnschedulablePodsDuration sets podMaxInUnschedulablePodsDuration for PriorityQueue.
func WithPodMaxInUnschedulablePodsDuration ( duration time . Duration ) Option {
2021-01-29 01:29:10 -05:00
return func ( o * priorityQueueOptions ) {
2023-06-08 00:54:30 -04:00
o . podMaxInUnschedulablePodsDuration = duration
2021-01-29 01:29:10 -05:00
}
}
2023-06-08 00:54:30 -04:00
// QueueingHintMapPerProfile is keyed with profile name, valued with queueing hint map registered for the profile.
type QueueingHintMapPerProfile map [ string ] QueueingHintMap
// QueueingHintMap is keyed with ClusterEvent, valued with queueing hint functions registered for the event.
type QueueingHintMap map [ framework . ClusterEvent ] [ ] * QueueingHintFunction
2023-11-27 22:44:35 -05:00
// WithQueueingHintMapPerProfile sets queueingHintMap for PriorityQueue.
2023-06-08 00:54:30 -04:00
func WithQueueingHintMapPerProfile ( m QueueingHintMapPerProfile ) Option {
2022-02-15 22:05:52 -05:00
return func ( o * priorityQueueOptions ) {
2023-06-08 00:54:30 -04:00
o . queueingHintMap = m
2022-02-15 22:05:52 -05:00
}
}
2022-11-07 17:02:22 -05:00
// WithPreEnqueuePluginMap sets preEnqueuePluginMap for PriorityQueue.
func WithPreEnqueuePluginMap ( m map [ string ] [ ] framework . PreEnqueuePlugin ) Option {
return func ( o * priorityQueueOptions ) {
o . preEnqueuePluginMap = m
}
}
2023-03-01 21:57:50 -05:00
// WithMetricsRecorder sets metrics recorder.
func WithMetricsRecorder ( recorder metrics . MetricAsyncRecorder ) Option {
return func ( o * priorityQueueOptions ) {
o . metricsRecorder = recorder
}
}
// WithPluginMetricsSamplePercent sets the percentage of plugin metrics to be sampled.
func WithPluginMetricsSamplePercent ( percent int ) Option {
return func ( o * priorityQueueOptions ) {
o . pluginMetricsSamplePercent = percent
}
}
2019-08-11 20:26:32 -04:00
var defaultPriorityQueueOptions = priorityQueueOptions {
2022-06-24 12:24:34 -04:00
clock : clock . RealClock { } ,
2022-03-24 05:38:49 -04:00
podInitialBackoffDuration : DefaultPodInitialBackoffDuration ,
podMaxBackoffDuration : DefaultPodMaxBackoffDuration ,
podMaxInUnschedulablePodsDuration : DefaultPodMaxInUnschedulablePodsDuration ,
2019-08-11 20:26:32 -04:00
}
2017-10-24 14:14:29 -04:00
// Making sure that PriorityQueue implements SchedulingQueue.
2019-10-17 22:58:44 -04:00
var _ SchedulingQueue = & PriorityQueue { }
2017-10-24 14:14:29 -04:00
2021-02-22 09:00:23 -05:00
// newQueuedPodInfoForLookup builds a QueuedPodInfo object for a lookup in the queue.
2021-01-29 01:29:10 -05:00
func newQueuedPodInfoForLookup ( pod * v1 . Pod , plugins ... string ) * framework . QueuedPodInfo {
2021-02-22 09:00:23 -05:00
// Since this is only used for a lookup in the queue, we only need to set the Pod,
// and so we avoid creating a full PodInfo, which is expensive to instantiate frequently.
2020-04-20 20:36:26 -04:00
return & framework . QueuedPodInfo {
2021-01-29 01:29:10 -05:00
PodInfo : & framework . PodInfo { Pod : pod } ,
2023-03-27 03:46:13 -04:00
UnschedulablePlugins : sets . New ( plugins ... ) ,
2019-02-21 20:46:18 -05:00
}
}
2018-02-08 01:42:19 -05:00
// NewPriorityQueue creates a PriorityQueue object.
2019-08-11 20:26:32 -04:00
func NewPriorityQueue (
2020-02-13 11:08:46 -05:00
lessFn framework . LessFunc ,
2021-03-04 07:30:24 -05:00
informerFactory informers . SharedInformerFactory ,
2019-08-11 20:26:32 -04:00
opts ... Option ,
) * PriorityQueue {
options := defaultPriorityQueueOptions
2023-03-08 16:18:36 -05:00
if options . podLister == nil {
options . podLister = informerFactory . Core ( ) . V1 ( ) . Pods ( ) . Lister ( )
}
2019-08-11 20:26:32 -04:00
for _ , opt := range opts {
opt ( & options )
}
2018-07-24 16:46:40 -04:00
2020-01-15 15:26:22 -05:00
comp := func ( podInfo1 , podInfo2 interface { } ) bool {
2020-04-20 20:36:26 -04:00
pInfo1 := podInfo1 . ( * framework . QueuedPodInfo )
pInfo2 := podInfo2 . ( * framework . QueuedPodInfo )
2020-02-13 11:08:46 -05:00
return lessFn ( pInfo1 , pInfo2 )
2019-05-06 21:03:00 -04:00
}
2017-10-24 14:14:29 -04:00
pq := & PriorityQueue {
2023-03-08 16:18:36 -05:00
nominator : newPodNominator ( options . podLister ) ,
2022-03-24 05:38:49 -04:00
clock : options . clock ,
stop : make ( chan struct { } ) ,
podInitialBackoffDuration : options . podInitialBackoffDuration ,
podMaxBackoffDuration : options . podMaxBackoffDuration ,
podMaxInUnschedulablePodsDuration : options . podMaxInUnschedulablePodsDuration ,
activeQ : heap . NewWithRecorder ( podInfoKeyFunc , comp , metrics . NewActivePodsRecorder ( ) ) ,
2022-11-07 17:02:22 -05:00
unschedulablePods : newUnschedulablePods ( metrics . NewUnschedulablePodsRecorder ( ) , metrics . NewGatedPodsRecorder ( ) ) ,
2023-09-04 09:48:45 -04:00
inFlightPods : make ( map [ types . UID ] * list . Element ) ,
inFlightEvents : list . New ( ) ,
2022-11-07 17:02:22 -05:00
preEnqueuePluginMap : options . preEnqueuePluginMap ,
2023-06-08 00:54:30 -04:00
queueingHintMap : options . queueingHintMap ,
2023-03-01 21:57:50 -05:00
metricsRecorder : options . metricsRecorder ,
pluginMetricsSamplePercent : options . pluginMetricsSamplePercent ,
2023-07-17 18:53:07 -04:00
moveRequestCycle : - 1 ,
isSchedulingQueueHintEnabled : utilfeature . DefaultFeatureGate . Enabled ( features . SchedulerQueueingHints ) ,
2017-10-24 14:14:29 -04:00
}
pq . cond . L = & pq . lock
2019-09-27 04:57:27 -04:00
pq . podBackoffQ = heap . NewWithRecorder ( podInfoKeyFunc , pq . podsCompareBackoffCompleted , metrics . NewBackoffPodsRecorder ( ) )
2022-02-15 13:08:48 -05:00
pq . nsLister = informerFactory . Core ( ) . V1 ( ) . Namespaces ( ) . Lister ( )
2018-07-24 16:46:40 -04:00
2017-10-24 14:14:29 -04:00
return pq
}
2019-12-02 14:35:09 -05:00
// Run starts the goroutine to pump from podBackoffQ to activeQ
2023-03-22 07:48:04 -04:00
func ( p * PriorityQueue ) Run ( logger klog . Logger ) {
go wait . Until ( func ( ) {
p . flushBackoffQCompleted ( logger )
} , 1.0 * time . Second , p . stop )
go wait . Until ( func ( ) {
p . flushUnschedulablePodsLeftover ( logger )
} , 30 * time . Second , p . stop )
2018-07-24 16:46:40 -04:00
}
2023-10-19 07:02:11 -04:00
// queueingStrategy indicates how the scheduling queue should enqueue the Pod from unschedulable pod pool.
type queueingStrategy int
const (
// queueSkip indicates that the scheduling queue should skip requeuing the Pod to activeQ/backoffQ.
queueSkip queueingStrategy = iota
// queueAfterBackoff indicates that the scheduling queue should requeue the Pod after backoff is completed.
queueAfterBackoff
// queueImmediately indicates that the scheduling queue should skip backoff and requeue the Pod immediately to activeQ.
queueImmediately
)
// isPodWorthRequeuing calls QueueingHintFn of only plugins registered in pInfo.unschedulablePlugins and pInfo.PendingPlugins.
//
// If any of pInfo.PendingPlugins return Queue,
// the scheduling queue is supposed to enqueue this Pod to activeQ, skipping backoffQ.
// If any of pInfo.unschedulablePlugins return Queue,
2023-06-08 00:54:30 -04:00
// the scheduling queue is supposed to enqueue this Pod to activeQ/backoffQ depending on the remaining backoff time of the Pod.
2023-10-19 07:02:11 -04:00
// If all QueueingHintFns returns Skip, the scheduling queue enqueues the Pod back to unschedulable Pod pool
2023-06-08 00:54:30 -04:00
// because no plugin changes the scheduling result via the event.
2023-10-19 07:02:11 -04:00
func ( p * PriorityQueue ) isPodWorthRequeuing ( logger klog . Logger , pInfo * framework . QueuedPodInfo , event framework . ClusterEvent , oldObj , newObj interface { } ) queueingStrategy {
2023-10-25 08:01:07 -04:00
rejectorPlugins := pInfo . UnschedulablePlugins . Union ( pInfo . PendingPlugins )
if rejectorPlugins . Len ( ) == 0 {
2023-10-19 07:02:11 -04:00
logger . V ( 6 ) . Info ( "Worth requeuing because no failed plugins" , "pod" , klog . KObj ( pInfo . Pod ) )
return queueAfterBackoff
2023-06-08 00:54:30 -04:00
}
if event . IsWildCard ( ) {
2023-10-19 07:02:11 -04:00
// If the wildcard event is special one as someone wants to force all Pods to move to activeQ/backoffQ.
// We return queueAfterBackoff in this case, while resetting all blocked plugins.
2023-06-08 00:54:30 -04:00
logger . V ( 6 ) . Info ( "Worth requeuing because the event is wildcard" , "pod" , klog . KObj ( pInfo . Pod ) )
2023-10-19 07:02:11 -04:00
return queueAfterBackoff
2023-06-08 00:54:30 -04:00
}
hintMap , ok := p . queueingHintMap [ pInfo . Pod . Spec . SchedulerName ]
if ! ok {
// shouldn't reach here unless bug.
logger . Error ( nil , "No QueueingHintMap is registered for this profile" , "profile" , pInfo . Pod . Spec . SchedulerName , "pod" , klog . KObj ( pInfo . Pod ) )
2023-10-19 07:02:11 -04:00
return queueAfterBackoff
2023-06-08 00:54:30 -04:00
}
pod := pInfo . Pod
2023-10-19 07:02:11 -04:00
queueStrategy := queueSkip
2023-06-08 00:54:30 -04:00
for eventToMatch , hintfns := range hintMap {
if eventToMatch . Resource != event . Resource || eventToMatch . ActionType & event . ActionType == 0 {
continue
}
for _ , hintfn := range hintfns {
2023-10-25 08:01:07 -04:00
if ! rejectorPlugins . Has ( hintfn . PluginName ) {
// skip if it's not hintfn from rejectorPlugins.
2023-06-08 00:54:30 -04:00
continue
}
2023-10-19 22:53:06 -04:00
hint , err := hintfn . QueueingHintFn ( logger , pod , oldObj , newObj )
2023-07-13 09:45:26 -04:00
if err != nil {
2023-10-19 07:02:11 -04:00
// If the QueueingHintFn returned an error, we should treat the event as Queue so that we can prevent
// the Pod from being stuck in the unschedulable pod pool.
2023-07-13 09:45:26 -04:00
oldObjMeta , newObjMeta , asErr := util . As [ klog . KMetadata ] ( oldObj , newObj )
if asErr != nil {
logger . Error ( err , "QueueingHintFn returns error" , "event" , event , "plugin" , hintfn . PluginName , "pod" , klog . KObj ( pod ) )
} else {
logger . Error ( err , "QueueingHintFn returns error" , "event" , event , "plugin" , hintfn . PluginName , "pod" , klog . KObj ( pod ) , "oldObj" , klog . KObj ( oldObjMeta ) , "newObj" , klog . KObj ( newObjMeta ) )
}
2023-10-19 22:53:06 -04:00
hint = framework . Queue
2023-07-13 09:45:26 -04:00
}
2023-10-19 22:53:06 -04:00
if hint == framework . QueueSkip {
2023-06-08 00:54:30 -04:00
continue
}
2023-10-19 07:02:11 -04:00
if pInfo . PendingPlugins . Has ( hintfn . PluginName ) {
// interprets Queue from the Pending plugin as queueImmediately.
// We can return immediately because queueImmediately is the highest priority.
return queueImmediately
}
// interprets Queue from the unschedulable plugin as queueAfterBackoff.
if pInfo . PendingPlugins . Len ( ) == 0 {
// We can return immediately because no Pending plugins, which only can make queueImmediately, registered in this Pod,
// and queueAfterBackoff is the second highest priority.
return queueAfterBackoff
}
// We can't return immediately because there are some Pending plugins registered in this Pod.
// We need to check if those plugins return Queue or not and if they do, we return queueImmediately.
queueStrategy = queueAfterBackoff
2023-06-08 00:54:30 -04:00
}
}
2023-10-19 07:02:11 -04:00
return queueStrategy
2023-06-08 00:54:30 -04:00
}
2022-11-07 17:02:22 -05:00
// runPreEnqueuePlugins iterates PreEnqueue function in each registered PreEnqueuePlugin.
// It returns true if all PreEnqueue function run successfully; otherwise returns false
// upon the first failure.
// Note: we need to associate the failed plugin to `pInfo`, so that the pod can be moved back
// to activeQ by related cluster event.
func ( p * PriorityQueue ) runPreEnqueuePlugins ( ctx context . Context , pInfo * framework . QueuedPodInfo ) bool {
2023-03-22 07:48:04 -04:00
logger := klog . FromContext ( ctx )
2022-11-07 17:02:22 -05:00
var s * framework . Status
pod := pInfo . Pod
2023-06-23 20:18:32 -04:00
startTime := p . clock . Now ( )
2022-11-07 17:02:22 -05:00
defer func ( ) {
metrics . FrameworkExtensionPointDuration . WithLabelValues ( preEnqueue , s . Code ( ) . String ( ) , pod . Spec . SchedulerName ) . Observe ( metrics . SinceInSeconds ( startTime ) )
} ( )
2023-03-01 21:57:50 -05:00
shouldRecordMetric := rand . Intn ( 100 ) < p . pluginMetricsSamplePercent
2022-11-07 17:02:22 -05:00
for _ , pl := range p . preEnqueuePluginMap [ pod . Spec . SchedulerName ] {
2023-03-01 21:57:50 -05:00
s = p . runPreEnqueuePlugin ( ctx , pl , pod , shouldRecordMetric )
2022-11-07 17:02:22 -05:00
if s . IsSuccess ( ) {
continue
}
pInfo . UnschedulablePlugins . Insert ( pl . Name ( ) )
metrics . UnschedulableReason ( pl . Name ( ) , pod . Spec . SchedulerName ) . Inc ( )
if s . Code ( ) == framework . Error {
2023-03-22 07:48:04 -04:00
logger . Error ( s . AsError ( ) , "Unexpected error running PreEnqueue plugin" , "pod" , klog . KObj ( pod ) , "plugin" , pl . Name ( ) )
2022-11-07 17:02:22 -05:00
} else {
2023-11-01 05:01:36 -04:00
logger . V ( 4 ) . Info ( "Status after running PreEnqueue plugin" , "pod" , klog . KObj ( pod ) , "plugin" , pl . Name ( ) , "status" , s )
2022-11-07 17:02:22 -05:00
}
return false
}
return true
}
2023-03-01 21:57:50 -05:00
func ( p * PriorityQueue ) runPreEnqueuePlugin ( ctx context . Context , pl framework . PreEnqueuePlugin , pod * v1 . Pod , shouldRecordMetric bool ) * framework . Status {
if ! shouldRecordMetric {
return pl . PreEnqueue ( ctx , pod )
}
startTime := p . clock . Now ( )
s := pl . PreEnqueue ( ctx , pod )
p . metricsRecorder . ObservePluginDurationAsync ( preEnqueue , pl . Name ( ) , s . Code ( ) . String ( ) , p . clock . Since ( startTime ) . Seconds ( ) )
return s
}
2022-11-07 17:02:22 -05:00
// addToActiveQ tries to add pod to active queue. It returns 2 parameters:
// 1. a boolean flag to indicate whether the pod is added successfully.
// 2. an error for the caller to act on.
2023-03-22 07:48:04 -04:00
func ( p * PriorityQueue ) addToActiveQ ( logger klog . Logger , pInfo * framework . QueuedPodInfo ) ( bool , error ) {
2022-11-07 17:02:22 -05:00
pInfo . Gated = ! p . runPreEnqueuePlugins ( context . Background ( ) , pInfo )
if pInfo . Gated {
// Add the Pod to unschedulablePods if it's not passing PreEnqueuePlugins.
p . unschedulablePods . addOrUpdate ( pInfo )
return false , nil
}
2023-05-15 15:20:44 -04:00
if pInfo . InitialAttemptTimestamp == nil {
now := p . clock . Now ( )
pInfo . InitialAttemptTimestamp = & now
}
2022-11-07 17:02:22 -05:00
if err := p . activeQ . Add ( pInfo ) ; err != nil {
2023-03-22 07:48:04 -04:00
logger . Error ( err , "Error adding pod to the active queue" , "pod" , klog . KObj ( pInfo . Pod ) )
2022-11-07 17:02:22 -05:00
return false , err
}
return true , nil
}
2017-10-24 14:14:29 -04:00
// Add adds a pod to the active queue. It should be called only when a new pod
2018-07-24 16:46:40 -04:00
// is added so there is no chance the pod is already in active/unschedulable/backoff queues
2023-03-22 07:48:04 -04:00
func ( p * PriorityQueue ) Add ( logger klog . Logger , pod * v1 . Pod ) error {
2017-10-24 14:14:29 -04:00
p . lock . Lock ( )
defer p . lock . Unlock ( )
2022-11-07 17:02:22 -05:00
2020-04-20 20:36:26 -04:00
pInfo := p . newQueuedPodInfo ( pod )
2022-11-22 00:33:16 -05:00
gated := pInfo . Gated
2023-03-22 07:48:04 -04:00
if added , err := p . addToActiveQ ( logger , pInfo ) ; ! added {
2018-12-14 19:31:08 -05:00
return err
2017-10-24 14:14:29 -04:00
}
2022-03-24 05:38:49 -04:00
if p . unschedulablePods . get ( pod ) != nil {
2023-03-22 07:48:04 -04:00
logger . Error ( nil , "Error: pod is already in the unschedulable queue" , "pod" , klog . KObj ( pod ) )
2022-11-22 00:33:16 -05:00
p . unschedulablePods . delete ( pod , gated )
2018-12-14 19:31:08 -05:00
}
// Delete pod from backoffQ if it is backing off
2019-02-21 20:46:18 -05:00
if err := p . podBackoffQ . Delete ( pInfo ) ; err == nil {
2023-03-22 07:48:04 -04:00
logger . Error ( nil , "Error: pod is already in the podBackoff queue" , "pod" , klog . KObj ( pod ) )
2018-12-14 19:31:08 -05:00
}
2023-06-05 12:02:06 -04:00
logger . V ( 5 ) . Info ( "Pod moved to an internal scheduling queue" , "pod" , klog . KObj ( pod ) , "event" , PodAdd , "queue" , activeQ )
2019-10-07 13:29:53 -04:00
metrics . SchedulerQueueIncomingPods . WithLabelValues ( "active" , PodAdd ) . Inc ( )
2023-03-22 07:48:04 -04:00
p . addNominatedPodUnlocked ( logger , pInfo . PodInfo , nil )
2018-12-14 19:31:08 -05:00
p . cond . Broadcast ( )
return nil
2017-10-24 14:14:29 -04:00
}
2022-03-24 05:38:49 -04:00
// Activate moves the given pods to activeQ iff they're in unschedulablePods or backoffQ.
2023-03-22 07:48:04 -04:00
func ( p * PriorityQueue ) Activate ( logger klog . Logger , pods map [ string ] * v1 . Pod ) {
2021-06-30 12:57:53 -04:00
p . lock . Lock ( )
defer p . lock . Unlock ( )
activated := false
for _ , pod := range pods {
2023-03-22 07:48:04 -04:00
if p . activate ( logger , pod ) {
2021-06-30 12:57:53 -04:00
activated = true
}
}
if activated {
p . cond . Broadcast ( )
}
}
2023-03-22 07:48:04 -04:00
func ( p * PriorityQueue ) activate ( logger klog . Logger , pod * v1 . Pod ) bool {
2021-06-30 12:57:53 -04:00
// Verify if the pod is present in activeQ.
if _ , exists , _ := p . activeQ . Get ( newQueuedPodInfoForLookup ( pod ) ) ; exists {
// No need to activate if it's already present in activeQ.
return false
}
var pInfo * framework . QueuedPodInfo
2022-03-24 05:38:49 -04:00
// Verify if the pod is present in unschedulablePods or backoffQ.
if pInfo = p . unschedulablePods . get ( pod ) ; pInfo == nil {
// If the pod doesn't belong to unschedulablePods or backoffQ, don't activate it.
2021-06-30 12:57:53 -04:00
if obj , exists , _ := p . podBackoffQ . Get ( newQueuedPodInfoForLookup ( pod ) ) ; ! exists {
2023-03-22 07:48:04 -04:00
logger . Error ( nil , "To-activate pod does not exist in unschedulablePods or backoffQ" , "pod" , klog . KObj ( pod ) )
2021-06-30 12:57:53 -04:00
return false
} else {
pInfo = obj . ( * framework . QueuedPodInfo )
}
}
if pInfo == nil {
// Redundant safe check. We shouldn't reach here.
2023-03-22 07:48:04 -04:00
logger . Error ( nil , "Internal error: cannot obtain pInfo" )
2021-06-30 12:57:53 -04:00
return false
}
2022-11-22 00:33:16 -05:00
gated := pInfo . Gated
2023-03-22 07:48:04 -04:00
if added , _ := p . addToActiveQ ( logger , pInfo ) ; ! added {
2021-06-30 12:57:53 -04:00
return false
}
2022-11-22 00:33:16 -05:00
p . unschedulablePods . delete ( pInfo . Pod , gated )
2021-06-30 12:57:53 -04:00
p . podBackoffQ . Delete ( pInfo )
metrics . SchedulerQueueIncomingPods . WithLabelValues ( "active" , ForceActivate ) . Inc ( )
2023-03-22 07:48:04 -04:00
p . addNominatedPodUnlocked ( logger , pInfo . PodInfo , nil )
2021-06-30 12:57:53 -04:00
return true
}
2020-02-13 19:31:07 -05:00
// isPodBackingoff returns true if a pod is still waiting for its backoff timer.
2019-02-15 20:43:08 -05:00
// If this returns true, the pod should not be re-tried.
2020-04-20 20:36:26 -04:00
func ( p * PriorityQueue ) isPodBackingoff ( podInfo * framework . QueuedPodInfo ) bool {
2022-11-08 02:11:34 -05:00
if podInfo . Gated {
return false
}
2020-02-13 19:31:07 -05:00
boTime := p . getBackoffTime ( podInfo )
2018-07-24 16:46:40 -04:00
return boTime . After ( p . clock . Now ( ) )
}
2019-01-25 05:39:53 -05:00
// SchedulingCycle returns current scheduling cycle.
func ( p * PriorityQueue ) SchedulingCycle ( ) int64 {
p . lock . RLock ( )
defer p . lock . RUnlock ( )
return p . schedulingCycle
}
2023-07-17 18:53:07 -04:00
// determineSchedulingHintForInFlightPod looks at the unschedulable plugins of the given Pod
// and determines the scheduling hint for this Pod while checking the events that happened during in-flight.
2023-10-19 07:02:11 -04:00
func ( p * PriorityQueue ) determineSchedulingHintForInFlightPod ( logger klog . Logger , pInfo * framework . QueuedPodInfo ) queueingStrategy {
2023-09-04 09:48:45 -04:00
logger . V ( 5 ) . Info ( "Checking events for in-flight pod" , "pod" , klog . KObj ( pInfo . Pod ) , "unschedulablePlugins" , pInfo . UnschedulablePlugins , "inFlightEventsSize" , p . inFlightEvents . Len ( ) , "inFlightPodsSize" , len ( p . inFlightPods ) )
// AddUnschedulableIfNotPresent is called with the Pod at the end of scheduling or binding.
// So, given pInfo should have been Pop()ed before,
// we can assume pInfo must be recorded in inFlightPods and thus inFlightEvents.
inFlightPod , ok := p . inFlightPods [ pInfo . Pod . UID ]
if ! ok {
// This can happen while updating a pod. In that case pInfo.UnschedulablePlugins should
// be empty. If it is not, we may have a problem.
if len ( pInfo . UnschedulablePlugins ) != 0 {
logger . Error ( nil , "In flight Pod isn't found in the scheduling queue. If you see this error log, it's likely a bug in the scheduler." , "pod" , klog . KObj ( pInfo . Pod ) )
2023-10-19 07:02:11 -04:00
return queueAfterBackoff
2023-09-04 09:48:45 -04:00
}
if p . inFlightEvents . Len ( ) > len ( p . inFlightPods ) {
2023-10-19 07:02:11 -04:00
return queueAfterBackoff
2023-07-17 18:53:07 -04:00
}
2023-10-19 07:02:11 -04:00
return queueSkip
2023-07-17 18:53:07 -04:00
}
2023-10-25 08:01:07 -04:00
rejectorPlugins := pInfo . UnschedulablePlugins . Union ( pInfo . PendingPlugins )
if len ( rejectorPlugins ) == 0 {
2023-10-19 07:02:11 -04:00
// No failed plugins are associated with this Pod.
2023-09-11 12:30:11 -04:00
// Meaning something unusual (a temporal failure on kube-apiserver, etc) happened and this Pod gets moved back to the queue.
// In this case, we should retry scheduling it because this Pod may not be retried until the next flush.
2023-10-19 07:02:11 -04:00
return queueAfterBackoff
2023-07-17 18:53:07 -04:00
}
// check if there is an event that makes this Pod schedulable based on pInfo.UnschedulablePlugins.
2023-10-19 07:02:11 -04:00
queueingStrategy := queueSkip
2023-09-04 09:48:45 -04:00
for event := inFlightPod . Next ( ) ; event != nil ; event = event . Next ( ) {
e , ok := event . Value . ( * clusterEvent )
if ! ok {
2023-10-19 07:02:11 -04:00
// Must be another in-flight Pod (*v1.Pod). Can be ignored.
2023-09-04 09:48:45 -04:00
continue
}
logger . V ( 5 ) . Info ( "Checking event for in-flight pod" , "pod" , klog . KObj ( pInfo . Pod ) , "event" , e . event . Label )
2023-07-17 18:53:07 -04:00
2023-10-19 07:02:11 -04:00
switch p . isPodWorthRequeuing ( logger , pInfo , e . event , e . oldObj , e . newObj ) {
case queueSkip :
2023-07-17 18:53:07 -04:00
continue
2023-10-19 07:02:11 -04:00
case queueImmediately :
// queueImmediately is the highest priority.
// No need to go through the rest of the events.
return queueImmediately
case queueAfterBackoff :
// replace schedulingHint with queueAfterBackoff
queueingStrategy = queueAfterBackoff
if pInfo . PendingPlugins . Len ( ) == 0 {
// We can return immediately because no Pending plugins, which only can make queueImmediately, registered in this Pod,
// and queueAfterBackoff is the second highest priority.
return queueAfterBackoff
}
2023-07-17 18:53:07 -04:00
}
}
2023-10-19 07:02:11 -04:00
return queueingStrategy
2023-07-17 18:53:07 -04:00
}
// addUnschedulableIfNotPresentWithoutQueueingHint inserts a pod that cannot be scheduled into
// the queue, unless it is already in the queue. Normally, PriorityQueue puts
// unschedulable pods in `unschedulablePods`. But if there has been a recent move
// request, then the pod is put in `podBackoffQ`.
// TODO: This function is called only when p.isSchedulingQueueHintEnabled is false,
// and this will be removed after SchedulingQueueHint goes to stable and the feature gate is removed.
func ( p * PriorityQueue ) addUnschedulableWithoutQueueingHint ( logger klog . Logger , pInfo * framework . QueuedPodInfo , podSchedulingCycle int64 ) error {
pod := pInfo . Pod
// Refresh the timestamp since the pod is re-added.
pInfo . Timestamp = p . clock . Now ( )
2023-10-19 07:02:11 -04:00
// When the queueing hint is enabled, they are used differently.
// But, we use all of them as UnschedulablePlugins when the queueing hint isn't enabled so that we don't break the old behaviour.
2023-10-25 08:01:07 -04:00
rejectorPlugins := pInfo . UnschedulablePlugins . Union ( pInfo . PendingPlugins )
2023-10-19 07:02:11 -04:00
2023-07-17 18:53:07 -04:00
// If a move request has been received, move it to the BackoffQ, otherwise move
// it to unschedulablePods.
2023-10-25 08:01:07 -04:00
for plugin := range rejectorPlugins {
2023-07-17 18:53:07 -04:00
metrics . UnschedulableReason ( plugin , pInfo . Pod . Spec . SchedulerName ) . Inc ( )
}
2023-10-25 08:01:07 -04:00
if p . moveRequestCycle >= podSchedulingCycle || len ( rejectorPlugins ) == 0 {
2023-09-11 12:30:11 -04:00
// Two cases to move a Pod to the active/backoff queue:
// - The Pod is rejected by some plugins, but a move request is received after this Pod's scheduling cycle is started.
// In this case, the received event may be make Pod schedulable and we should retry scheduling it.
// - No unschedulable plugins are associated with this Pod,
// meaning something unusual (a temporal failure on kube-apiserver, etc) happened and this Pod gets moved back to the queue.
// In this case, we should retry scheduling it because this Pod may not be retried until the next flush.
2023-07-17 18:53:07 -04:00
if err := p . podBackoffQ . Add ( pInfo ) ; err != nil {
return fmt . Errorf ( "error adding pod %v to the backoff queue: %v" , klog . KObj ( pod ) , err )
}
logger . V ( 5 ) . Info ( "Pod moved to an internal scheduling queue" , "pod" , klog . KObj ( pod ) , "event" , ScheduleAttemptFailure , "queue" , backoffQ )
metrics . SchedulerQueueIncomingPods . WithLabelValues ( "backoff" , ScheduleAttemptFailure ) . Inc ( )
} else {
p . unschedulablePods . addOrUpdate ( pInfo )
logger . V ( 5 ) . Info ( "Pod moved to an internal scheduling queue" , "pod" , klog . KObj ( pod ) , "event" , ScheduleAttemptFailure , "queue" , unschedulablePods )
metrics . SchedulerQueueIncomingPods . WithLabelValues ( "unschedulable" , ScheduleAttemptFailure ) . Inc ( )
}
p . addNominatedPodUnlocked ( logger , pInfo . PodInfo , nil )
return nil
}
2019-02-15 20:43:08 -05:00
// AddUnschedulableIfNotPresent inserts a pod that cannot be scheduled into
// the queue, unless it is already in the queue. Normally, PriorityQueue puts
2022-03-24 05:38:49 -04:00
// unschedulable pods in `unschedulablePods`. But if there has been a recent move
2019-02-15 20:43:08 -05:00
// request, then the pod is put in `podBackoffQ`.
2023-03-22 07:48:04 -04:00
func ( p * PriorityQueue ) AddUnschedulableIfNotPresent ( logger klog . Logger , pInfo * framework . QueuedPodInfo , podSchedulingCycle int64 ) error {
2017-10-24 14:14:29 -04:00
p . lock . Lock ( )
defer p . lock . Unlock ( )
2023-07-17 18:53:07 -04:00
// In any case, this Pod will be moved back to the queue and we should call Done.
defer p . done ( pInfo . Pod . UID )
2019-10-07 19:06:00 -04:00
pod := pInfo . Pod
2022-03-24 05:38:49 -04:00
if p . unschedulablePods . get ( pod ) != nil {
2021-01-25 02:07:39 -05:00
return fmt . Errorf ( "Pod %v is already present in unschedulable queue" , klog . KObj ( pod ) )
2017-10-24 14:14:29 -04:00
}
2019-02-21 20:46:18 -05:00
if _ , exists , _ := p . activeQ . Get ( pInfo ) ; exists {
2021-01-25 02:07:39 -05:00
return fmt . Errorf ( "Pod %v is already present in the active queue" , klog . KObj ( pod ) )
2017-10-24 14:14:29 -04:00
}
2019-02-21 20:46:18 -05:00
if _ , exists , _ := p . podBackoffQ . Get ( pInfo ) ; exists {
2021-01-25 02:07:39 -05:00
return fmt . Errorf ( "Pod %v is already present in the backoff queue" , klog . KObj ( pod ) )
2018-07-24 16:46:40 -04:00
}
2020-12-15 07:07:47 -05:00
2023-07-17 18:53:07 -04:00
if ! p . isSchedulingQueueHintEnabled {
// fall back to the old behavior which doesn't depend on the queueing hint.
return p . addUnschedulableWithoutQueueingHint ( logger , pInfo , podSchedulingCycle )
}
2020-12-14 09:39:08 -05:00
// Refresh the timestamp since the pod is re-added.
pInfo . Timestamp = p . clock . Now ( )
2018-07-24 16:46:40 -04:00
2019-02-15 20:43:08 -05:00
// If a move request has been received, move it to the BackoffQ, otherwise move
2022-03-24 05:38:49 -04:00
// it to unschedulablePods.
2023-10-25 08:01:07 -04:00
rejectorPlugins := pInfo . UnschedulablePlugins . Union ( pInfo . PendingPlugins )
for plugin := range rejectorPlugins {
2022-03-14 16:02:58 -04:00
metrics . UnschedulableReason ( plugin , pInfo . Pod . Spec . SchedulerName ) . Inc ( )
}
2023-07-17 18:53:07 -04:00
2023-09-11 12:30:11 -04:00
// We check whether this Pod may change its scheduling result by any of events that happened during scheduling.
schedulingHint := p . determineSchedulingHintForInFlightPod ( logger , pInfo )
2023-07-17 18:53:07 -04:00
// In this case, we try to requeue this Pod to activeQ/backoffQ.
queue := p . requeuePodViaQueueingHint ( logger , pInfo , schedulingHint , ScheduleAttemptFailure )
2023-10-25 08:01:07 -04:00
logger . V ( 3 ) . Info ( "Pod moved to an internal scheduling queue" , "pod" , klog . KObj ( pod ) , "event" , ScheduleAttemptFailure , "queue" , queue , "schedulingCycle" , podSchedulingCycle , "hint" , schedulingHint , "unschedulable plugins" , rejectorPlugins )
2023-08-06 04:21:47 -04:00
if queue == activeQ {
// When the Pod is moved to activeQ, need to let p.cond know so that the Pod will be pop()ed out.
p . cond . Broadcast ( )
}
2018-07-24 16:46:40 -04:00
2023-03-22 07:48:04 -04:00
p . addNominatedPodUnlocked ( logger , pInfo . PodInfo , nil )
2019-02-15 20:43:08 -05:00
return nil
2017-10-24 14:14:29 -04:00
}
2018-07-24 16:46:40 -04:00
// flushBackoffQCompleted Moves all pods from backoffQ which have completed backoff in to activeQ
2023-03-22 07:48:04 -04:00
func ( p * PriorityQueue ) flushBackoffQCompleted ( logger klog . Logger ) {
2018-07-24 16:46:40 -04:00
p . lock . Lock ( )
defer p . lock . Unlock ( )
2022-05-08 00:09:13 -04:00
activated := false
2018-07-24 16:46:40 -04:00
for {
2019-02-21 20:46:18 -05:00
rawPodInfo := p . podBackoffQ . Peek ( )
if rawPodInfo == nil {
2022-01-27 08:48:41 -05:00
break
2018-07-24 16:46:40 -04:00
}
2022-11-07 17:02:22 -05:00
pInfo := rawPodInfo . ( * framework . QueuedPodInfo )
pod := pInfo . Pod
if p . isPodBackingoff ( pInfo ) {
2022-01-27 08:48:41 -05:00
break
2018-07-24 16:46:40 -04:00
}
_ , err := p . podBackoffQ . Pop ( )
if err != nil {
2023-03-22 07:48:04 -04:00
logger . Error ( err , "Unable to pop pod from backoff queue despite backoff completion" , "pod" , klog . KObj ( pod ) )
2022-01-27 08:48:41 -05:00
break
2018-07-24 16:46:40 -04:00
}
2023-03-22 07:48:04 -04:00
if added , _ := p . addToActiveQ ( logger , pInfo ) ; added {
2023-06-05 12:02:06 -04:00
logger . V ( 5 ) . Info ( "Pod moved to an internal scheduling queue" , "pod" , klog . KObj ( pod ) , "event" , BackoffComplete , "queue" , activeQ )
2022-11-07 17:02:22 -05:00
metrics . SchedulerQueueIncomingPods . WithLabelValues ( "active" , BackoffComplete ) . Inc ( )
activated = true
}
2022-01-27 08:48:41 -05:00
}
2022-05-08 00:09:13 -04:00
if activated {
2022-01-27 04:08:12 -05:00
p . cond . Broadcast ( )
2018-07-24 16:46:40 -04:00
}
}
2022-03-24 05:38:49 -04:00
// flushUnschedulablePodsLeftover moves pods which stay in unschedulablePods
// longer than podMaxInUnschedulablePodsDuration to backoffQ or activeQ.
2023-03-22 07:48:04 -04:00
func ( p * PriorityQueue ) flushUnschedulablePodsLeftover ( logger klog . Logger ) {
2019-01-15 23:08:19 -05:00
p . lock . Lock ( )
defer p . lock . Unlock ( )
2020-04-20 20:36:26 -04:00
var podsToMove [ ] * framework . QueuedPodInfo
2019-01-15 23:08:19 -05:00
currentTime := p . clock . Now ( )
2022-03-24 05:38:49 -04:00
for _ , pInfo := range p . unschedulablePods . podInfoMap {
2019-05-06 21:03:00 -04:00
lastScheduleTime := pInfo . Timestamp
2022-03-24 05:38:49 -04:00
if currentTime . Sub ( lastScheduleTime ) > p . podMaxInUnschedulablePodsDuration {
2019-02-21 20:46:18 -05:00
podsToMove = append ( podsToMove , pInfo )
2019-01-15 23:08:19 -05:00
}
}
if len ( podsToMove ) > 0 {
2023-06-08 00:54:30 -04:00
p . movePodsToActiveOrBackoffQueue ( logger , podsToMove , UnschedulableTimeout , nil , nil )
2019-01-15 23:08:19 -05:00
}
}
2017-10-24 14:14:29 -04:00
// Pop removes the head of the active queue and returns it. It blocks if the
2019-01-25 05:39:53 -05:00
// activeQ is empty and waits until a new item is added to the queue. It
// increments scheduling cycle when a pod is popped.
2023-09-28 22:59:22 -04:00
func ( p * PriorityQueue ) Pop ( logger klog . Logger ) ( * framework . QueuedPodInfo , error ) {
2017-10-24 14:14:29 -04:00
p . lock . Lock ( )
defer p . lock . Unlock ( )
2017-12-09 18:09:48 -05:00
for p . activeQ . Len ( ) == 0 {
2018-09-14 19:59:54 -04:00
// When the queue is empty, invocation of Pop() is blocked until new item is enqueued.
// When Close() is called, the p.closed is set and the condition is broadcast,
// which causes this loop to continue and return from the Pop().
if p . closed {
2023-09-28 22:59:22 -04:00
logger . V ( 2 ) . Info ( "Scheduling queue is closed" )
2023-03-14 08:22:22 -04:00
return nil , nil
2018-09-14 19:59:54 -04:00
}
2017-10-24 14:14:29 -04:00
p . cond . Wait ( )
}
obj , err := p . activeQ . Pop ( )
if err != nil {
return nil , err
}
2020-04-20 20:36:26 -04:00
pInfo := obj . ( * framework . QueuedPodInfo )
2019-10-07 19:06:00 -04:00
pInfo . Attempts ++
2019-01-25 05:39:53 -05:00
p . schedulingCycle ++
2023-09-04 09:48:45 -04:00
// In flight, no concurrent events yet.
2023-07-17 18:53:07 -04:00
if p . isSchedulingQueueHintEnabled {
2023-09-04 09:48:45 -04:00
p . inFlightPods [ pInfo . Pod . UID ] = p . inFlightEvents . PushBack ( pInfo . Pod )
2023-07-17 18:53:07 -04:00
}
2023-09-01 01:55:31 -04:00
// Update metrics and reset the set of unschedulable plugins for the next attempt.
2023-10-19 07:02:11 -04:00
for plugin := range pInfo . UnschedulablePlugins . Union ( pInfo . PendingPlugins ) {
2023-07-17 18:53:07 -04:00
metrics . UnschedulableReason ( plugin , pInfo . Pod . Spec . SchedulerName ) . Dec ( )
}
2023-09-01 01:55:31 -04:00
pInfo . UnschedulablePlugins . Clear ( )
2023-10-19 07:02:11 -04:00
pInfo . PendingPlugins . Clear ( )
2023-07-17 18:53:07 -04:00
2021-12-15 08:51:42 -05:00
return pInfo , nil
2017-10-24 14:14:29 -04:00
}
2023-07-17 18:53:07 -04:00
// Done must be called for pod returned by Pop. This allows the queue to
// keep track of which pods are currently being processed.
func ( p * PriorityQueue ) Done ( pod types . UID ) {
p . lock . Lock ( )
defer p . lock . Unlock ( )
p . done ( pod )
}
func ( p * PriorityQueue ) done ( pod types . UID ) {
if ! p . isSchedulingQueueHintEnabled {
// do nothing if schedulingQueueHint is disabled.
2023-09-04 09:48:45 -04:00
// In that case, we don't have inFlightPods and inFlightEvents.
2023-07-17 18:53:07 -04:00
return
}
inFlightPod , ok := p . inFlightPods [ pod ]
if ! ok {
// This Pod is already done()ed.
return
}
delete ( p . inFlightPods , pod )
2023-09-04 09:48:45 -04:00
// Remove the pod from the list.
p . inFlightEvents . Remove ( inFlightPod )
// Remove events which are only referred to by this Pod
// so that the inFlightEvents list doesn't grow infinitely.
// If the pod was at the head of the list, then all
// events between it and the next pod are no longer needed
// and can be removed.
for {
e := p . inFlightEvents . Front ( )
if e == nil {
// Empty list.
break
}
if _ , ok := e . Value . ( * clusterEvent ) ; ! ok {
// A pod, must stop pruning.
break
2023-07-17 18:53:07 -04:00
}
2023-09-04 09:48:45 -04:00
p . inFlightEvents . Remove ( e )
2023-07-17 18:53:07 -04:00
}
}
2017-10-24 14:14:29 -04:00
// isPodUpdated checks if the pod is updated in a way that it may have become
2023-04-14 03:50:52 -04:00
// schedulable. It drops status of the pod and compares it with old version,
// except for pod.status.resourceClaimStatuses: changing that may have an
// effect on scheduling.
2017-10-24 14:14:29 -04:00
func isPodUpdated ( oldPod , newPod * v1 . Pod ) bool {
strip := func ( pod * v1 . Pod ) * v1 . Pod {
p := pod . DeepCopy ( )
p . ResourceVersion = ""
2017-11-07 20:09:21 -05:00
p . Generation = 0
2023-04-14 03:50:52 -04:00
p . Status = v1 . PodStatus {
ResourceClaimStatuses : pod . Status . ResourceClaimStatuses ,
}
2021-03-22 22:18:50 -04:00
p . ManagedFields = nil
p . Finalizers = nil
2017-10-24 14:14:29 -04:00
return p
}
return ! reflect . DeepEqual ( strip ( oldPod ) , strip ( newPod ) )
}
2018-12-23 08:24:52 -05:00
// Update updates a pod in the active or backoff queue if present. Otherwise, it removes
// the item from the unschedulable queue if pod is updated in a way that it may
// become schedulable and adds the updated one to the active queue.
// If pod is not present in any of the queues, it is added to the active queue.
2023-03-22 07:48:04 -04:00
func ( p * PriorityQueue ) Update ( logger klog . Logger , oldPod , newPod * v1 . Pod ) error {
2017-10-24 14:14:29 -04:00
p . lock . Lock ( )
defer p . lock . Unlock ( )
2018-07-24 16:46:40 -04:00
if oldPod != nil {
2021-02-22 09:00:23 -05:00
oldPodInfo := newQueuedPodInfoForLookup ( oldPod )
2018-07-24 16:46:40 -04:00
// If the pod is already in the active queue, just update it there.
2019-02-21 20:46:18 -05:00
if oldPodInfo , exists , _ := p . activeQ . Get ( oldPodInfo ) ; exists {
2021-02-22 09:00:23 -05:00
pInfo := updatePod ( oldPodInfo , newPod )
2023-03-22 07:48:04 -04:00
p . updateNominatedPodUnlocked ( logger , oldPod , pInfo . PodInfo )
2021-02-22 09:00:23 -05:00
return p . activeQ . Update ( pInfo )
2018-07-24 16:46:40 -04:00
}
// If the pod is in the backoff queue, update it there.
2019-02-21 20:46:18 -05:00
if oldPodInfo , exists , _ := p . podBackoffQ . Get ( oldPodInfo ) ; exists {
2021-02-22 09:00:23 -05:00
pInfo := updatePod ( oldPodInfo , newPod )
2023-03-22 07:48:04 -04:00
p . updateNominatedPodUnlocked ( logger , oldPod , pInfo . PodInfo )
2021-02-06 04:08:16 -05:00
return p . podBackoffQ . Update ( pInfo )
2018-07-24 16:46:40 -04:00
}
2017-10-24 14:14:29 -04:00
}
2018-07-24 16:46:40 -04:00
2017-10-24 14:14:29 -04:00
// If the pod is in the unschedulable queue, updating it may make it schedulable.
2022-03-24 05:38:49 -04:00
if usPodInfo := p . unschedulablePods . get ( newPod ) ; usPodInfo != nil {
2021-02-06 04:08:16 -05:00
pInfo := updatePod ( usPodInfo , newPod )
2023-03-22 07:48:04 -04:00
p . updateNominatedPodUnlocked ( logger , oldPod , pInfo . PodInfo )
2018-02-08 21:19:31 -05:00
if isPodUpdated ( oldPod , newPod ) {
2022-11-22 00:33:16 -05:00
gated := usPodInfo . Gated
2021-02-06 04:08:16 -05:00
if p . isPodBackingoff ( usPodInfo ) {
if err := p . podBackoffQ . Add ( pInfo ) ; err != nil {
return err
}
2022-11-22 00:33:16 -05:00
p . unschedulablePods . delete ( usPodInfo . Pod , gated )
2023-06-05 12:02:06 -04:00
logger . V ( 5 ) . Info ( "Pod moved to an internal scheduling queue" , "pod" , klog . KObj ( pInfo . Pod ) , "event" , PodUpdate , "queue" , backoffQ )
2021-02-06 04:08:16 -05:00
} else {
2023-03-22 07:48:04 -04:00
if added , err := p . addToActiveQ ( logger , pInfo ) ; ! added {
2021-02-06 04:08:16 -05:00
return err
}
2022-11-22 00:33:16 -05:00
p . unschedulablePods . delete ( usPodInfo . Pod , gated )
2023-06-05 12:02:06 -04:00
logger . V ( 5 ) . Info ( "Pod moved to an internal scheduling queue" , "pod" , klog . KObj ( pInfo . Pod ) , "event" , BackoffComplete , "queue" , activeQ )
2021-02-06 04:08:16 -05:00
p . cond . Broadcast ( )
2017-10-24 14:14:29 -04:00
}
2021-02-06 04:08:16 -05:00
} else {
// Pod update didn't make it schedulable, keep it in the unschedulable queue.
2022-03-24 05:38:49 -04:00
p . unschedulablePods . addOrUpdate ( pInfo )
2017-10-24 14:14:29 -04:00
}
2021-02-06 04:08:16 -05:00
2018-02-08 01:42:19 -05:00
return nil
2017-10-24 14:14:29 -04:00
}
2018-12-23 08:24:52 -05:00
// If pod is not in any of the queues, we put it in the active queue.
2021-02-22 09:00:23 -05:00
pInfo := p . newQueuedPodInfo ( newPod )
2023-03-22 07:48:04 -04:00
if added , err := p . addToActiveQ ( logger , pInfo ) ; ! added {
2021-02-22 09:00:23 -05:00
return err
2017-10-24 14:14:29 -04:00
}
2023-03-22 07:48:04 -04:00
p . addNominatedPodUnlocked ( logger , pInfo . PodInfo , nil )
2023-06-05 12:02:06 -04:00
logger . V ( 5 ) . Info ( "Pod moved to an internal scheduling queue" , "pod" , klog . KObj ( pInfo . Pod ) , "event" , PodUpdate , "queue" , activeQ )
2021-02-22 09:00:23 -05:00
p . cond . Broadcast ( )
return nil
2017-10-24 14:14:29 -04:00
}
// Delete deletes the item from either of the two queues. It assumes the pod is
// only in one queue.
func ( p * PriorityQueue ) Delete ( pod * v1 . Pod ) error {
p . lock . Lock ( )
defer p . lock . Unlock ( )
2023-03-08 16:18:36 -05:00
p . deleteNominatedPodIfExistsUnlocked ( pod )
2022-11-07 17:02:22 -05:00
pInfo := newQueuedPodInfoForLookup ( pod )
if err := p . activeQ . Delete ( pInfo ) ; err != nil {
2021-02-22 09:00:23 -05:00
// The item was probably not found in the activeQ.
2022-11-07 17:02:22 -05:00
p . podBackoffQ . Delete ( pInfo )
2022-11-22 00:33:16 -05:00
if pInfo = p . unschedulablePods . get ( pod ) ; pInfo != nil {
p . unschedulablePods . delete ( pod , pInfo . Gated )
}
2017-10-24 14:14:29 -04:00
}
return nil
}
// AssignedPodAdded is called when a bound pod is added. Creation of this pod
// may make pending pods with matching affinity terms schedulable.
2023-03-22 07:48:04 -04:00
func ( p * PriorityQueue ) AssignedPodAdded ( logger klog . Logger , pod * v1 . Pod ) {
2018-11-16 17:22:15 -05:00
p . lock . Lock ( )
2023-06-08 00:54:30 -04:00
p . movePodsToActiveOrBackoffQueue ( logger , p . getUnschedulablePodsWithMatchingAffinityTerm ( logger , pod ) , AssignedPodAdd , nil , pod )
2018-11-16 17:22:15 -05:00
p . lock . Unlock ( )
2017-10-24 14:14:29 -04:00
}
2022-11-04 16:53:49 -04:00
// isPodResourcesResizedDown returns true if a pod CPU and/or memory resize request has been
// admitted by kubelet, is 'InProgress', and results in a net sizing down of updated resources.
// It returns false if either CPU or memory resource is net resized up, or if no resize is in progress.
func isPodResourcesResizedDown ( pod * v1 . Pod ) bool {
if utilfeature . DefaultFeatureGate . Enabled ( features . InPlacePodVerticalScaling ) {
// TODO(vinaykul,wangchen615,InPlacePodVerticalScaling): Fix this to determine when a
// pod is truly resized down (might need oldPod if we cannot determine from Status alone)
if pod . Status . Resize == v1 . PodResizeStatusInProgress {
return true
}
}
return false
}
2017-10-24 14:14:29 -04:00
// AssignedPodUpdated is called when a bound pod is updated. Change of labels
// may make pending pods with matching affinity terms schedulable.
2023-06-08 00:54:30 -04:00
func ( p * PriorityQueue ) AssignedPodUpdated ( logger klog . Logger , oldPod , newPod * v1 . Pod ) {
2018-11-16 17:22:15 -05:00
p . lock . Lock ( )
2023-06-08 00:54:30 -04:00
if isPodResourcesResizedDown ( newPod ) {
p . moveAllToActiveOrBackoffQueue ( logger , AssignedPodUpdate , oldPod , newPod , nil )
2022-11-04 16:53:49 -04:00
} else {
2023-06-08 00:54:30 -04:00
p . movePodsToActiveOrBackoffQueue ( logger , p . getUnschedulablePodsWithMatchingAffinityTerm ( logger , newPod ) , AssignedPodUpdate , oldPod , newPod )
2022-11-04 16:53:49 -04:00
}
2018-11-16 17:22:15 -05:00
p . lock . Unlock ( )
2017-10-24 14:14:29 -04:00
}
2022-11-04 16:53:49 -04:00
// NOTE: this function assumes a lock has been acquired in the caller.
// moveAllToActiveOrBackoffQueue moves all pods from unschedulablePods to activeQ or backoffQ.
2020-01-02 10:35:12 -05:00
// This function adds all pods and then signals the condition variable to ensure that
2021-08-05 08:15:29 -04:00
// if Pop() is waiting for an item, it receives the signal after all the pods are in the
2017-10-24 14:14:29 -04:00
// queue and the head is the highest priority pod.
2023-06-08 00:54:30 -04:00
func ( p * PriorityQueue ) moveAllToActiveOrBackoffQueue ( logger klog . Logger , event framework . ClusterEvent , oldObj , newObj interface { } , preCheck PreEnqueueCheck ) {
2022-03-24 05:38:49 -04:00
unschedulablePods := make ( [ ] * framework . QueuedPodInfo , 0 , len ( p . unschedulablePods . podInfoMap ) )
for _ , pInfo := range p . unschedulablePods . podInfoMap {
2021-03-11 15:31:33 -05:00
if preCheck == nil || preCheck ( pInfo . Pod ) {
unschedulablePods = append ( unschedulablePods , pInfo )
}
2019-06-02 02:42:52 -04:00
}
2023-06-08 00:54:30 -04:00
p . movePodsToActiveOrBackoffQueue ( logger , unschedulablePods , event , oldObj , newObj )
2017-10-24 14:14:29 -04:00
}
2022-11-04 16:53:49 -04:00
// MoveAllToActiveOrBackoffQueue moves all pods from unschedulablePods to activeQ or backoffQ.
// This function adds all pods and then signals the condition variable to ensure that
// if Pop() is waiting for an item, it receives the signal after all the pods are in the
// queue and the head is the highest priority pod.
2023-06-08 00:54:30 -04:00
func ( p * PriorityQueue ) MoveAllToActiveOrBackoffQueue ( logger klog . Logger , event framework . ClusterEvent , oldObj , newObj interface { } , preCheck PreEnqueueCheck ) {
2022-11-04 16:53:49 -04:00
p . lock . Lock ( )
defer p . lock . Unlock ( )
2023-06-08 00:54:30 -04:00
p . moveAllToActiveOrBackoffQueue ( logger , event , oldObj , newObj , preCheck )
}
// requeuePodViaQueueingHint tries to requeue Pod to activeQ, backoffQ or unschedulable pod pool based on schedulingHint.
// It returns the queue name Pod goes.
//
// NOTE: this function assumes lock has been acquired in caller
2023-10-19 07:02:11 -04:00
func ( p * PriorityQueue ) requeuePodViaQueueingHint ( logger klog . Logger , pInfo * framework . QueuedPodInfo , strategy queueingStrategy , event string ) string {
if strategy == queueSkip {
2023-06-08 00:54:30 -04:00
p . unschedulablePods . addOrUpdate ( pInfo )
2023-07-17 18:53:07 -04:00
metrics . SchedulerQueueIncomingPods . WithLabelValues ( "unschedulable" , event ) . Inc ( )
2023-06-08 00:54:30 -04:00
return unschedulablePods
}
pod := pInfo . Pod
2023-10-19 07:02:11 -04:00
if strategy == queueAfterBackoff && p . isPodBackingoff ( pInfo ) {
2023-06-08 00:54:30 -04:00
if err := p . podBackoffQ . Add ( pInfo ) ; err != nil {
logger . Error ( err , "Error adding pod to the backoff queue, queue this Pod to unschedulable pod pool" , "pod" , klog . KObj ( pod ) )
p . unschedulablePods . addOrUpdate ( pInfo )
return unschedulablePods
}
metrics . SchedulerQueueIncomingPods . WithLabelValues ( "backoff" , event ) . Inc ( )
return backoffQ
}
2023-10-19 07:02:11 -04:00
// Reach here if schedulingHint is QueueImmediately, or schedulingHint is Queue but the pod is not backing off.
2023-07-04 11:00:12 -04:00
added , err := p . addToActiveQ ( logger , pInfo )
if err != nil {
logger . Error ( err , "Error adding pod to the active queue, queue this Pod to unschedulable pod pool" , "pod" , klog . KObj ( pod ) )
}
if added {
2023-06-08 00:54:30 -04:00
metrics . SchedulerQueueIncomingPods . WithLabelValues ( "active" , event ) . Inc ( )
return activeQ
}
2023-07-04 11:00:12 -04:00
if pInfo . Gated {
// In case the pod is gated, the Pod is pushed back to unschedulable Pods pool in addToActiveQ.
return unschedulablePods
}
2023-06-08 00:54:30 -04:00
p . unschedulablePods . addOrUpdate ( pInfo )
metrics . SchedulerQueueIncomingPods . WithLabelValues ( "unschedulable" , ScheduleAttemptFailure ) . Inc ( )
return unschedulablePods
2022-11-04 16:53:49 -04:00
}
2018-11-16 17:22:15 -05:00
// NOTE: this function assumes lock has been acquired in caller
2023-06-08 00:54:30 -04:00
func ( p * PriorityQueue ) movePodsToActiveOrBackoffQueue ( logger klog . Logger , podInfoList [ ] * framework . QueuedPodInfo , event framework . ClusterEvent , oldObj , newObj interface { } ) {
2022-05-08 00:09:13 -04:00
activated := false
2019-02-21 20:46:18 -05:00
for _ , pInfo := range podInfoList {
2023-06-08 00:54:30 -04:00
schedulingHint := p . isPodWorthRequeuing ( logger , pInfo , event , oldObj , newObj )
2023-10-19 07:02:11 -04:00
if schedulingHint == queueSkip {
2023-06-08 00:54:30 -04:00
// QueueingHintFn determined that this Pod isn't worth putting to activeQ or backoffQ by this event.
logger . V ( 5 ) . Info ( "Event is not making pod schedulable" , "pod" , klog . KObj ( pInfo . Pod ) , "event" , event . Label )
2021-01-29 01:29:10 -05:00
continue
}
2023-06-08 00:54:30 -04:00
p . unschedulablePods . delete ( pInfo . Pod , pInfo . Gated )
queue := p . requeuePodViaQueueingHint ( logger , pInfo , schedulingHint , event . Label )
logger . V ( 4 ) . Info ( "Pod moved to an internal scheduling queue" , "pod" , klog . KObj ( pInfo . Pod ) , "event" , event . Label , "queue" , queue , "hint" , schedulingHint )
if queue == activeQ {
activated = true
2018-02-19 21:18:16 -05:00
}
2017-10-24 14:14:29 -04:00
}
2023-07-17 18:53:07 -04:00
2019-01-25 05:39:53 -05:00
p . moveRequestCycle = p . schedulingCycle
2023-07-17 18:53:07 -04:00
2023-09-11 12:30:11 -04:00
if p . isSchedulingQueueHintEnabled && len ( p . inFlightPods ) != 0 {
2023-09-04 09:48:45 -04:00
logger . V ( 5 ) . Info ( "Event received while pods are in flight" , "event" , event . Label , "numPods" , len ( p . inFlightPods ) )
2023-07-17 18:53:07 -04:00
// AddUnschedulableIfNotPresent might get called for in-flight Pods later, and in
// AddUnschedulableIfNotPresent we need to know whether events were
// observed while scheduling them.
2023-09-04 09:48:45 -04:00
p . inFlightEvents . PushBack ( & clusterEvent {
event : event ,
oldObj : oldObj ,
newObj : newObj ,
2023-07-17 18:53:07 -04:00
} )
}
2022-05-08 00:09:13 -04:00
if activated {
2021-01-29 01:29:10 -05:00
p . cond . Broadcast ( )
}
2017-10-24 14:14:29 -04:00
}
// getUnschedulablePodsWithMatchingAffinityTerm returns unschedulable pods which have
// any affinity term that matches "pod".
2018-11-16 17:22:15 -05:00
// NOTE: this function assumes lock has been acquired in caller.
2023-06-01 06:23:29 -04:00
func ( p * PriorityQueue ) getUnschedulablePodsWithMatchingAffinityTerm ( logger klog . Logger , pod * v1 . Pod ) [ ] * framework . QueuedPodInfo {
nsLabels := interpodaffinity . GetNamespaceLabelsSnapshot ( logger , pod . Namespace , p . nsLister )
2022-02-15 13:08:48 -05:00
2020-04-20 20:36:26 -04:00
var podsToMove [ ] * framework . QueuedPodInfo
2022-03-24 05:38:49 -04:00
for _ , pInfo := range p . unschedulablePods . podInfoMap {
2021-03-04 07:30:24 -05:00
for _ , term := range pInfo . RequiredAffinityTerms {
2022-02-15 13:08:48 -05:00
if term . Matches ( pod , nsLabels ) {
2020-05-12 15:18:05 -04:00
podsToMove = append ( podsToMove , pInfo )
break
2017-10-24 14:14:29 -04:00
}
}
2020-05-12 15:18:05 -04:00
2017-10-24 14:14:29 -04:00
}
return podsToMove
}
2022-08-05 21:30:09 -04:00
var pendingPodsSummary = "activeQ:%v; backoffQ:%v; unschedulablePods:%v"
// PendingPods returns all the pending pods in the queue; accompanied by a debugging string
// recording showing the number of pods in each queue respectively.
// This function is used for debugging purposes in the scheduler cache dumper and comparer.
func ( p * PriorityQueue ) PendingPods ( ) ( [ ] * v1 . Pod , string ) {
2019-04-06 16:48:49 -04:00
p . lock . RLock ( )
defer p . lock . RUnlock ( )
2020-04-30 18:51:25 -04:00
var result [ ] * v1 . Pod
2019-02-21 20:46:18 -05:00
for _ , pInfo := range p . activeQ . List ( ) {
2020-04-20 20:36:26 -04:00
result = append ( result , pInfo . ( * framework . QueuedPodInfo ) . Pod )
2018-03-12 17:43:01 -04:00
}
2019-02-21 20:46:18 -05:00
for _ , pInfo := range p . podBackoffQ . List ( ) {
2020-04-20 20:36:26 -04:00
result = append ( result , pInfo . ( * framework . QueuedPodInfo ) . Pod )
2018-12-20 20:28:23 -05:00
}
2022-03-24 05:38:49 -04:00
for _ , pInfo := range p . unschedulablePods . podInfoMap {
2019-05-06 21:03:00 -04:00
result = append ( result , pInfo . Pod )
2018-03-12 17:43:01 -04:00
}
2022-08-05 21:30:09 -04:00
return result , fmt . Sprintf ( pendingPodsSummary , p . activeQ . Len ( ) , p . podBackoffQ . Len ( ) , len ( p . unschedulablePods . podInfoMap ) )
2018-03-12 17:43:01 -04:00
}
2018-09-14 19:59:54 -04:00
// Close closes the priority queue.
func ( p * PriorityQueue ) Close ( ) {
p . lock . Lock ( )
defer p . lock . Unlock ( )
2019-12-02 14:35:09 -05:00
close ( p . stop )
2018-09-14 19:59:54 -04:00
p . closed = true
p . cond . Broadcast ( )
}
2020-05-18 13:29:08 -04:00
// DeleteNominatedPodIfExists deletes <pod> from nominatedPods.
2021-06-05 14:30:17 -04:00
func ( npm * nominator ) DeleteNominatedPodIfExists ( pod * v1 . Pod ) {
2023-03-08 16:18:36 -05:00
npm . lock . Lock ( )
npm . deleteNominatedPodIfExistsUnlocked ( pod )
npm . lock . Unlock ( )
}
func ( npm * nominator ) deleteNominatedPodIfExistsUnlocked ( pod * v1 . Pod ) {
2020-05-18 13:29:08 -04:00
npm . delete ( pod )
2018-12-18 02:41:53 -05:00
}
2020-05-18 13:29:08 -04:00
// AddNominatedPod adds a pod to the nominated pods of the given node.
2018-12-18 02:41:53 -05:00
// This is called during the preemption process after a node is nominated to run
// the pod. We update the structure before sending a request to update the pod
// object to avoid races with the following scheduling cycles.
2023-03-22 07:48:04 -04:00
func ( npm * nominator ) AddNominatedPod ( logger klog . Logger , pi * framework . PodInfo , nominatingInfo * framework . NominatingInfo ) {
2023-03-08 16:18:36 -05:00
npm . lock . Lock ( )
2023-03-22 07:48:04 -04:00
npm . addNominatedPodUnlocked ( logger , pi , nominatingInfo )
2023-03-08 16:18:36 -05:00
npm . lock . Unlock ( )
2018-11-16 17:22:15 -05:00
}
2021-07-08 23:56:43 -04:00
// NominatedPodsForNode returns a copy of pods that are nominated to run on the given node,
2020-05-20 14:38:21 -04:00
// but they are waiting for other pods to be removed from the node.
2021-06-05 14:30:17 -04:00
func ( npm * nominator ) NominatedPodsForNode ( nodeName string ) [ ] * framework . PodInfo {
2023-03-08 16:18:36 -05:00
npm . lock . RLock ( )
defer npm . lock . RUnlock ( )
2021-07-08 23:56:43 -04:00
// Make a copy of the nominated Pods so the caller can mutate safely.
pods := make ( [ ] * framework . PodInfo , len ( npm . nominatedPods [ nodeName ] ) )
for i := 0 ; i < len ( pods ) ; i ++ {
pods [ i ] = npm . nominatedPods [ nodeName ] [ i ] . DeepCopy ( )
}
return pods
2020-05-20 14:38:21 -04:00
}
2019-02-21 20:46:18 -05:00
func ( p * PriorityQueue ) podsCompareBackoffCompleted ( podInfo1 , podInfo2 interface { } ) bool {
2020-04-20 20:36:26 -04:00
pInfo1 := podInfo1 . ( * framework . QueuedPodInfo )
pInfo2 := podInfo2 . ( * framework . QueuedPodInfo )
2020-02-13 19:31:07 -05:00
bo1 := p . getBackoffTime ( pInfo1 )
bo2 := p . getBackoffTime ( pInfo2 )
2018-07-24 16:46:40 -04:00
return bo1 . Before ( bo2 )
}
2020-04-20 20:36:26 -04:00
// newQueuedPodInfo builds a QueuedPodInfo object.
2021-01-29 01:29:10 -05:00
func ( p * PriorityQueue ) newQueuedPodInfo ( pod * v1 . Pod , plugins ... string ) * framework . QueuedPodInfo {
2019-10-07 19:06:00 -04:00
now := p . clock . Now ( )
2022-09-05 00:43:45 -04:00
// ignore this err since apiserver doesn't properly validate affinity terms
// and we can't fix the validation for backwards compatibility.
podInfo , _ := framework . NewPodInfo ( pod )
2020-04-20 20:36:26 -04:00
return & framework . QueuedPodInfo {
2022-09-05 00:43:45 -04:00
PodInfo : podInfo ,
2019-10-07 19:06:00 -04:00
Timestamp : now ,
2023-05-15 15:20:44 -04:00
InitialAttemptTimestamp : nil ,
2023-03-27 03:46:13 -04:00
UnschedulablePlugins : sets . New ( plugins ... ) ,
2019-02-21 20:46:18 -05:00
}
2018-11-29 02:21:13 -05:00
}
2020-02-13 19:31:07 -05:00
// getBackoffTime returns the time that podInfo completes backoff
2020-04-20 20:36:26 -04:00
func ( p * PriorityQueue ) getBackoffTime ( podInfo * framework . QueuedPodInfo ) time . Time {
2020-02-13 19:31:07 -05:00
duration := p . calculateBackoffDuration ( podInfo )
backoffTime := podInfo . Timestamp . Add ( duration )
return backoffTime
}
// calculateBackoffDuration is a helper function for calculating the backoffDuration
// based on the number of attempts the pod has made.
2020-04-20 20:36:26 -04:00
func ( p * PriorityQueue ) calculateBackoffDuration ( podInfo * framework . QueuedPodInfo ) time . Duration {
2020-02-13 19:31:07 -05:00
duration := p . podInitialBackoffDuration
for i := 1 ; i < podInfo . Attempts ; i ++ {
2021-09-18 22:09:06 -04:00
// Use subtraction instead of addition or multiplication to avoid overflow.
if duration > p . podMaxBackoffDuration - duration {
2020-02-13 19:31:07 -05:00
return p . podMaxBackoffDuration
}
2021-09-18 22:09:06 -04:00
duration += duration
2020-02-13 19:31:07 -05:00
}
return duration
}
2020-04-20 20:36:26 -04:00
func updatePod ( oldPodInfo interface { } , newPod * v1 . Pod ) * framework . QueuedPodInfo {
pInfo := oldPodInfo . ( * framework . QueuedPodInfo )
2021-02-22 09:00:23 -05:00
pInfo . Update ( newPod )
2019-10-07 19:06:00 -04:00
return pInfo
}
2022-03-24 05:38:49 -04:00
// UnschedulablePods holds pods that cannot be scheduled. This data structure
// is used to implement unschedulablePods.
type UnschedulablePods struct {
2020-04-20 20:36:26 -04:00
// podInfoMap is a map key by a pod's full-name and the value is a pointer to the QueuedPodInfo.
podInfoMap map [ string ] * framework . QueuedPodInfo
2019-02-21 20:46:18 -05:00
keyFunc func ( * v1 . Pod ) string
2022-11-07 17:02:22 -05:00
// unschedulableRecorder/gatedRecorder updates the counter when elements of an unschedulablePodsMap
// get added or removed, and it does nothing if it's nil.
unschedulableRecorder , gatedRecorder metrics . MetricRecorder
2017-10-24 14:14:29 -04:00
}
2022-11-22 00:33:16 -05:00
// addOrUpdate adds a pod to the unschedulable podInfoMap.
2022-03-24 05:38:49 -04:00
func ( u * UnschedulablePods ) addOrUpdate ( pInfo * framework . QueuedPodInfo ) {
2019-05-06 21:03:00 -04:00
podID := u . keyFunc ( pInfo . Pod )
2022-11-07 17:02:22 -05:00
if _ , exists := u . podInfoMap [ podID ] ; ! exists {
if pInfo . Gated && u . gatedRecorder != nil {
u . gatedRecorder . Inc ( )
} else if ! pInfo . Gated && u . unschedulableRecorder != nil {
u . unschedulableRecorder . Inc ( )
}
2019-03-19 02:43:43 -04:00
}
u . podInfoMap [ podID ] = pInfo
2017-10-24 14:14:29 -04:00
}
2022-11-22 00:33:16 -05:00
// delete deletes a pod from the unschedulable podInfoMap.
// The `gated` parameter is used to figure out which metric should be decreased.
func ( u * UnschedulablePods ) delete ( pod * v1 . Pod , gated bool ) {
podID := u . keyFunc ( pod )
2022-11-07 17:02:22 -05:00
if _ , exists := u . podInfoMap [ podID ] ; exists {
2022-11-22 00:33:16 -05:00
if gated && u . gatedRecorder != nil {
2022-11-07 17:02:22 -05:00
u . gatedRecorder . Dec ( )
2022-11-22 00:33:16 -05:00
} else if ! gated && u . unschedulableRecorder != nil {
2022-11-07 17:02:22 -05:00
u . unschedulableRecorder . Dec ( )
}
2019-03-19 02:43:43 -04:00
}
delete ( u . podInfoMap , podID )
2017-10-24 14:14:29 -04:00
}
2022-11-22 00:33:16 -05:00
// get returns the QueuedPodInfo if a pod with the same key as the key of the given "pod"
2017-10-24 14:14:29 -04:00
// is found in the map. It returns nil otherwise.
2022-03-24 05:38:49 -04:00
func ( u * UnschedulablePods ) get ( pod * v1 . Pod ) * framework . QueuedPodInfo {
2017-10-24 14:14:29 -04:00
podKey := u . keyFunc ( pod )
2019-02-21 20:46:18 -05:00
if pInfo , exists := u . podInfoMap [ podKey ] ; exists {
return pInfo
2017-10-24 14:14:29 -04:00
}
return nil
}
2022-11-22 00:33:16 -05:00
// clear removes all the entries from the unschedulable podInfoMap.
2022-03-24 05:38:49 -04:00
func ( u * UnschedulablePods ) clear ( ) {
2020-04-20 20:36:26 -04:00
u . podInfoMap = make ( map [ string ] * framework . QueuedPodInfo )
2022-11-07 17:02:22 -05:00
if u . unschedulableRecorder != nil {
u . unschedulableRecorder . Clear ( )
}
if u . gatedRecorder != nil {
u . gatedRecorder . Clear ( )
2019-03-19 02:43:43 -04:00
}
2017-10-24 14:14:29 -04:00
}
2022-03-24 05:38:49 -04:00
// newUnschedulablePods initializes a new object of UnschedulablePods.
2022-11-07 17:02:22 -05:00
func newUnschedulablePods ( unschedulableRecorder , gatedRecorder metrics . MetricRecorder ) * UnschedulablePods {
2022-03-24 05:38:49 -04:00
return & UnschedulablePods {
2022-11-07 17:02:22 -05:00
podInfoMap : make ( map [ string ] * framework . QueuedPodInfo ) ,
keyFunc : util . GetPodFullName ,
unschedulableRecorder : unschedulableRecorder ,
gatedRecorder : gatedRecorder ,
2017-10-24 14:14:29 -04:00
}
}
2018-12-18 02:41:53 -05:00
2021-06-05 14:30:17 -04:00
// nominator is a structure that stores pods nominated to run on nodes.
2018-12-18 02:41:53 -05:00
// It exists because nominatedNodeName of pod objects stored in the structure
// may be different than what scheduler has here. We should be able to find pods
// by their UID and update/delete them.
2021-06-05 14:30:17 -04:00
type nominator struct {
2021-06-04 14:04:44 -04:00
// podLister is used to verify if the given pod is alive.
podLister listersv1 . PodLister
2018-12-18 02:41:53 -05:00
// nominatedPods is a map keyed by a node name and the value is a list of
// pods which are nominated to run on the node. These are pods which can be in
2022-03-24 05:38:49 -04:00
// the activeQ or unschedulablePods.
2021-02-22 09:00:23 -05:00
nominatedPods map [ string ] [ ] * framework . PodInfo
2018-12-18 02:41:53 -05:00
// nominatedPodToNode is map keyed by a Pod UID to the node name where it is
// nominated.
2021-04-09 03:31:32 -04:00
nominatedPodToNode map [ types . UID ] string
2020-05-18 13:29:08 -04:00
2023-03-08 16:18:36 -05:00
lock sync . RWMutex
2018-12-18 02:41:53 -05:00
}
2023-03-22 07:48:04 -04:00
func ( npm * nominator ) addNominatedPodUnlocked ( logger klog . Logger , pi * framework . PodInfo , nominatingInfo * framework . NominatingInfo ) {
2021-12-16 13:54:58 -05:00
// Always delete the pod if it already exists, to ensure we never store more than
2018-12-18 02:41:53 -05:00
// one instance of the pod.
2021-02-22 09:00:23 -05:00
npm . delete ( pi . Pod )
2018-12-18 02:41:53 -05:00
2021-12-16 13:54:58 -05:00
var nodeName string
if nominatingInfo . Mode ( ) == framework . ModeOverride {
nodeName = nominatingInfo . NominatedNodeName
} else if nominatingInfo . Mode ( ) == framework . ModeNoop {
if pi . Pod . Status . NominatedNodeName == "" {
2018-12-18 02:41:53 -05:00
return
}
2021-12-16 13:54:58 -05:00
nodeName = pi . Pod . Status . NominatedNodeName
2018-12-18 02:41:53 -05:00
}
2021-06-04 14:04:44 -04:00
if npm . podLister != nil {
2022-04-01 15:21:10 -04:00
// If the pod was removed or if it was already scheduled, don't nominate it.
updatedPod , err := npm . podLister . Pods ( pi . Pod . Namespace ) . Get ( pi . Pod . Name )
if err != nil {
2023-03-22 07:48:04 -04:00
logger . V ( 4 ) . Info ( "Pod doesn't exist in podLister, aborted adding it to the nominator" , "pod" , klog . KObj ( pi . Pod ) )
2022-04-01 15:21:10 -04:00
return
}
if updatedPod . Spec . NodeName != "" {
2023-03-22 07:48:04 -04:00
logger . V ( 4 ) . Info ( "Pod is already scheduled to a node, aborted adding it to the nominator" , "pod" , klog . KObj ( pi . Pod ) , "node" , updatedPod . Spec . NodeName )
2021-06-04 14:04:44 -04:00
return
}
}
2021-12-16 13:54:58 -05:00
npm . nominatedPodToNode [ pi . Pod . UID ] = nodeName
for _ , npi := range npm . nominatedPods [ nodeName ] {
2021-02-22 09:00:23 -05:00
if npi . Pod . UID == pi . Pod . UID {
2023-03-22 07:48:04 -04:00
logger . V ( 4 ) . Info ( "Pod already exists in the nominator" , "pod" , klog . KObj ( npi . Pod ) )
2018-12-18 02:41:53 -05:00
return
}
}
2021-12-16 13:54:58 -05:00
npm . nominatedPods [ nodeName ] = append ( npm . nominatedPods [ nodeName ] , pi )
2018-12-18 02:41:53 -05:00
}
2021-06-05 14:30:17 -04:00
func ( npm * nominator ) delete ( p * v1 . Pod ) {
2018-12-18 02:41:53 -05:00
nnn , ok := npm . nominatedPodToNode [ p . UID ]
if ! ok {
return
}
for i , np := range npm . nominatedPods [ nnn ] {
2021-02-22 09:00:23 -05:00
if np . Pod . UID == p . UID {
2018-12-18 02:41:53 -05:00
npm . nominatedPods [ nnn ] = append ( npm . nominatedPods [ nnn ] [ : i ] , npm . nominatedPods [ nnn ] [ i + 1 : ] ... )
if len ( npm . nominatedPods [ nnn ] ) == 0 {
delete ( npm . nominatedPods , nnn )
}
break
}
}
delete ( npm . nominatedPodToNode , p . UID )
}
2020-05-18 13:29:08 -04:00
// UpdateNominatedPod updates the <oldPod> with <newPod>.
2023-03-22 07:48:04 -04:00
func ( npm * nominator ) UpdateNominatedPod ( logger klog . Logger , oldPod * v1 . Pod , newPodInfo * framework . PodInfo ) {
2023-03-08 16:18:36 -05:00
npm . lock . Lock ( )
defer npm . lock . Unlock ( )
2023-03-22 07:48:04 -04:00
npm . updateNominatedPodUnlocked ( logger , oldPod , newPodInfo )
2023-03-08 16:18:36 -05:00
}
2023-03-22 07:48:04 -04:00
func ( npm * nominator ) updateNominatedPodUnlocked ( logger klog . Logger , oldPod * v1 . Pod , newPodInfo * framework . PodInfo ) {
2019-05-16 02:12:33 -04:00
// In some cases, an Update event with no "NominatedNode" present is received right
// after a node("NominatedNode") is reserved for this pod in memory.
2019-05-17 02:43:26 -04:00
// In this case, we need to keep reserving the NominatedNode when updating the pod pointer.
2021-12-16 13:54:58 -05:00
var nominatingInfo * framework . NominatingInfo
2019-05-17 02:43:26 -04:00
// We won't fall into below `if` block if the Update event represents:
// (1) NominatedNode info is added
// (2) NominatedNode info is updated
// (3) NominatedNode info is removed
2021-02-22 09:00:23 -05:00
if NominatedNodeName ( oldPod ) == "" && NominatedNodeName ( newPodInfo . Pod ) == "" {
2019-05-17 02:43:26 -04:00
if nnn , ok := npm . nominatedPodToNode [ oldPod . UID ] ; ok {
// This is the only case we should continue reserving the NominatedNode
2021-12-16 13:54:58 -05:00
nominatingInfo = & framework . NominatingInfo {
NominatingMode : framework . ModeOverride ,
NominatedNodeName : nnn ,
}
2019-05-17 02:43:26 -04:00
}
2019-05-16 02:12:33 -04:00
}
2019-05-17 02:43:26 -04:00
// We update irrespective of the nominatedNodeName changed or not, to ensure
2018-12-18 02:41:53 -05:00
// that pod pointer is updated.
npm . delete ( oldPod )
2023-03-22 07:48:04 -04:00
npm . addNominatedPodUnlocked ( logger , newPodInfo , nominatingInfo )
2018-12-18 02:41:53 -05:00
}
2021-06-05 14:30:17 -04:00
// NewPodNominator creates a nominator as a backing of framework.PodNominator.
// A podLister is passed in so as to check if the pod exists
2021-06-04 14:04:44 -04:00
// before adding its nominatedNode info.
2021-06-05 14:30:17 -04:00
func NewPodNominator ( podLister listersv1 . PodLister ) framework . PodNominator {
2023-03-08 16:18:36 -05:00
return newPodNominator ( podLister )
}
func newPodNominator ( podLister listersv1 . PodLister ) * nominator {
2021-06-05 14:30:17 -04:00
return & nominator {
2021-06-04 14:04:44 -04:00
podLister : podLister ,
2021-02-22 09:00:23 -05:00
nominatedPods : make ( map [ string ] [ ] * framework . PodInfo ) ,
2021-04-09 03:31:32 -04:00
nominatedPodToNode : make ( map [ types . UID ] string ) ,
2018-12-18 02:41:53 -05:00
}
}
2019-01-07 04:54:49 -05:00
2019-02-21 20:46:18 -05:00
func podInfoKeyFunc ( obj interface { } ) ( string , error ) {
2020-04-20 20:36:26 -04:00
return cache . MetaNamespaceKeyFunc ( obj . ( * framework . QueuedPodInfo ) . Pod )
2019-02-21 20:46:18 -05:00
}