2024-07-22 06:13:13 -04:00
/ *
Copyright 2024 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 .
* /
package queue
import (
"container/list"
"fmt"
"sync"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
2025-07-07 21:34:49 -04:00
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2024-07-22 06:13:13 -04:00
"k8s.io/klog/v2"
2025-06-26 11:06:29 -04:00
fwk "k8s.io/kube-scheduler/framework"
2024-08-25 00:10:29 -04:00
"k8s.io/kubernetes/pkg/scheduler/backend/heap"
2024-07-22 06:13:13 -04:00
"k8s.io/kubernetes/pkg/scheduler/framework"
"k8s.io/kubernetes/pkg/scheduler/metrics"
)
// activeQueuer is a wrapper for activeQ related operations.
2025-08-06 03:35:46 -04:00
// Its methods take the lock inside.
2024-07-22 06:13:13 -04:00
type activeQueuer interface {
2024-08-20 04:51:21 -04:00
underLock ( func ( unlockedActiveQ unlockedActiveQueuer ) )
2024-08-20 05:21:06 -04:00
underRLock ( func ( unlockedActiveQ unlockedActiveQueueReader ) )
2024-07-22 06:13:13 -04:00
2024-08-20 05:21:06 -04:00
delete ( pInfo * framework . QueuedPodInfo ) error
2024-07-22 06:13:13 -04:00
pop ( logger klog . Logger ) ( * framework . QueuedPodInfo , error )
list ( ) [ ] * v1 . Pod
len ( ) int
has ( pInfo * framework . QueuedPodInfo ) bool
listInFlightEvents ( ) [ ] interface { }
listInFlightPods ( ) [ ] * v1 . Pod
clusterEventsForPod ( logger klog . Logger , pInfo * framework . QueuedPodInfo ) ( [ ] * clusterEvent , error )
2025-06-26 11:06:29 -04:00
addEventsIfPodInFlight ( oldPod , newPod * v1 . Pod , events [ ] fwk . ClusterEvent ) bool
addEventIfAnyInFlight ( oldObj , newObj interface { } , event fwk . ClusterEvent ) bool
2024-07-22 06:13:13 -04:00
schedulingCycle ( ) int64
done ( pod types . UID )
close ( )
broadcast ( )
}
// unlockedActiveQueuer defines activeQ methods that are not protected by the lock itself.
2024-08-20 05:21:06 -04:00
// underLock() method should be used to protect these methods.
2024-07-22 06:13:13 -04:00
type unlockedActiveQueuer interface {
2024-08-20 05:21:06 -04:00
unlockedActiveQueueReader
2025-02-25 08:05:33 -05:00
// add adds a new pod to the activeQ.
// The event should show which event triggered this addition and is used for the metric recording.
// This method should be called in activeQueue.underLock().
2025-09-02 05:35:22 -04:00
add ( logger klog . Logger , pInfo * framework . QueuedPodInfo , event string )
2025-08-06 03:35:46 -04:00
// update updates the pod in activeQ if oldPodInfo is already in the queue.
// It returns new pod info if updated, nil otherwise.
update ( newPod * v1 . Pod , oldPodInfo * framework . QueuedPodInfo ) * framework . QueuedPodInfo
// addEventsIfPodInFlight adds events to inFlightEvents if the newPod is in inFlightPods.
// It returns true if pushed the event to the inFlightEvents.
addEventsIfPodInFlight ( oldPod , newPod * v1 . Pod , events [ ] fwk . ClusterEvent ) bool
2024-08-20 05:21:06 -04:00
}
// unlockedActiveQueueReader defines activeQ read-only methods that are not protected by the lock itself.
// underLock() or underRLock() method should be used to protect these methods.
type unlockedActiveQueueReader interface {
2025-02-25 08:05:33 -05:00
// get returns the pod matching pInfo inside the activeQ.
// Returns false if the pInfo doesn't exist in the queue.
// This method should be called in activeQueue.underLock() or activeQueue.underRLock().
get ( pInfo * framework . QueuedPodInfo ) ( * framework . QueuedPodInfo , bool )
// has returns if pInfo exists in the queue.
// This method should be called in activeQueue.underLock() or activeQueue.underRLock().
has ( pInfo * framework . QueuedPodInfo ) bool
}
// unlockedActiveQueue defines activeQ methods that are not protected by the lock itself.
// activeQueue.underLock() or activeQueue.underRLock() method should be used to protect these methods.
type unlockedActiveQueue struct {
2025-08-06 03:35:46 -04:00
queue * heap . Heap [ * framework . QueuedPodInfo ]
inFlightPods map [ types . UID ] * list . Element
inFlightEvents * list . List
metricsRecorder * metrics . MetricAsyncRecorder
2025-02-25 08:05:33 -05:00
}
2025-08-06 03:35:46 -04:00
func newUnlockedActiveQueue ( queue * heap . Heap [ * framework . QueuedPodInfo ] , inFlightPods map [ types . UID ] * list . Element , inFlightEvents * list . List , metricsRecorder * metrics . MetricAsyncRecorder ) * unlockedActiveQueue {
2025-02-25 08:05:33 -05:00
return & unlockedActiveQueue {
2025-08-06 03:35:46 -04:00
queue : queue ,
inFlightPods : inFlightPods ,
inFlightEvents : inFlightEvents ,
metricsRecorder : metricsRecorder ,
2025-02-25 08:05:33 -05:00
}
}
// add adds a new pod to the activeQ.
// The event should show which event triggered this addition and is used for the metric recording.
// This method should be called in activeQueue.underLock().
2025-09-02 05:35:22 -04:00
func ( uaq * unlockedActiveQueue ) add ( logger klog . Logger , pInfo * framework . QueuedPodInfo , event string ) {
2025-02-25 08:05:33 -05:00
uaq . queue . AddOrUpdate ( pInfo )
metrics . SchedulerQueueIncomingPods . WithLabelValues ( "active" , event ) . Inc ( )
2025-09-02 05:35:22 -04:00
logger . V ( 5 ) . Info ( "Pod moved to an internal scheduling queue" , "pod" , klog . KObj ( pInfo . Pod ) , "event" , event , "queue" , activeQ )
2025-02-25 08:05:33 -05:00
}
2025-08-06 03:35:46 -04:00
// update updates the pod in activeQ if oldPodInfo is already in the queue.
// It returns new pod info if updated, nil otherwise.
func ( uaq * unlockedActiveQueue ) update ( newPod * v1 . Pod , oldPodInfo * framework . QueuedPodInfo ) * framework . QueuedPodInfo {
if pInfo , exists := uaq . queue . Get ( oldPodInfo ) ; exists {
_ = pInfo . Update ( newPod )
uaq . queue . AddOrUpdate ( pInfo )
return pInfo
}
return nil
}
// addEventsIfPodInFlight adds events to inFlightEvents if the newPod is in inFlightPods.
// It returns true if pushed the event to the inFlightEvents.
func ( uaq * unlockedActiveQueue ) addEventsIfPodInFlight ( oldPod , newPod * v1 . Pod , events [ ] fwk . ClusterEvent ) bool {
_ , ok := uaq . inFlightPods [ newPod . UID ]
if ok {
for _ , event := range events {
uaq . metricsRecorder . ObserveInFlightEventsAsync ( event . Label ( ) , 1 , false )
uaq . inFlightEvents . PushBack ( & clusterEvent {
event : event ,
oldObj : oldPod ,
newObj : newPod ,
} )
}
}
return ok
}
2025-02-25 08:05:33 -05:00
// get returns the pod matching pInfo inside the activeQ.
// Returns false if the pInfo doesn't exist in the queue.
// This method should be called in activeQueue.underLock() or activeQueue.underRLock().
func ( uaq * unlockedActiveQueue ) get ( pInfo * framework . QueuedPodInfo ) ( * framework . QueuedPodInfo , bool ) {
return uaq . queue . Get ( pInfo )
}
// has returns if pInfo exists in the queue.
// This method should be called in activeQueue.underLock() or activeQueue.underRLock().
func ( uaq * unlockedActiveQueue ) has ( pInfo * framework . QueuedPodInfo ) bool {
return uaq . queue . Has ( pInfo )
2024-07-22 06:13:13 -04:00
}
2025-03-10 04:56:21 -04:00
// backoffQPopper defines method that is used to pop from the backoffQ when the activeQ is empty.
type backoffQPopper interface {
// popBackoff pops the pInfo from the podBackoffQ.
popBackoff ( ) ( * framework . QueuedPodInfo , error )
// len returns length of the podBackoffQ queue.
lenBackoff ( ) int
}
2024-07-22 06:13:13 -04:00
// activeQueue implements activeQueuer. All of the fields have to be protected using the lock.
type activeQueue struct {
// lock synchronizes all operations related to activeQ.
// It protects activeQ, inFlightPods, inFlightEvents, schedulingCycle and closed fields.
// Caution: DO NOT take "SchedulingQueue.lock" after taking "lock".
// You should always take "SchedulingQueue.lock" first, otherwise the queue could end up in deadlock.
2025-03-10 04:56:21 -04:00
// "lock" should not be taken after taking "backoffQueue.lock" or "nominator.nLock".
// Correct locking order is: SchedulingQueue.lock > lock > backoffQueue.lock > nominator.nLock.
2024-07-22 06:13:13 -04:00
lock sync . RWMutex
// activeQ is heap structure that scheduler actively looks at to find pods to
// schedule. Head of heap is the highest priority pod.
queue * heap . Heap [ * framework . QueuedPodInfo ]
2025-02-25 08:05:33 -05:00
// unlockedQueue is a wrapper of queue providing methods that are not locked themselves
// and can be used in the underLock() or underRLock().
unlockedQueue * unlockedActiveQueue
2024-07-22 06:13:13 -04:00
// cond is a condition that is notified when the pod is added to activeQ.
2025-03-10 04:56:21 -04:00
// When SchedulerPopFromBackoffQ feature is enabled,
// condition is also notified when the pod is added to backoffQ.
2024-07-22 06:13:13 -04:00
// It is used with lock.
cond sync . Cond
// 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).
//
// 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
// schedCycle represents sequence number of scheduling cycle and is incremented
// when a pod is popped.
schedCycle int64
// 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
// isSchedulingQueueHintEnabled indicates whether the feature gate for the scheduling queue is enabled.
isSchedulingQueueHintEnabled bool
2024-09-01 11:54:00 -04:00
2025-08-06 03:35:46 -04:00
metricsRecorder * metrics . MetricAsyncRecorder
2025-03-10 04:56:21 -04:00
// backoffQPopper is used to pop from backoffQ when activeQ is empty.
// It is non-nil only when SchedulerPopFromBackoffQ feature is enabled.
backoffQPopper backoffQPopper
2024-07-22 06:13:13 -04:00
}
2025-08-06 03:35:46 -04:00
func newActiveQueue ( queue * heap . Heap [ * framework . QueuedPodInfo ] , isSchedulingQueueHintEnabled bool , metricRecorder * metrics . MetricAsyncRecorder , backoffQPopper backoffQPopper ) * activeQueue {
2024-07-22 06:13:13 -04:00
aq := & activeQueue {
queue : queue ,
inFlightPods : make ( map [ types . UID ] * list . Element ) ,
inFlightEvents : list . New ( ) ,
isSchedulingQueueHintEnabled : isSchedulingQueueHintEnabled ,
2024-09-01 11:54:00 -04:00
metricsRecorder : metricRecorder ,
2025-03-10 04:56:21 -04:00
backoffQPopper : backoffQPopper ,
2024-07-22 06:13:13 -04:00
}
aq . cond . L = & aq . lock
2025-08-06 03:35:46 -04:00
aq . unlockedQueue = newUnlockedActiveQueue ( queue , aq . inFlightPods , aq . inFlightEvents , metricRecorder )
2024-07-22 06:13:13 -04:00
return aq
}
2024-08-20 04:51:21 -04:00
// underLock runs the fn function under the lock.Lock.
// fn can run unlockedActiveQueuer methods but should NOT run any other activeQueue method,
// as it would end up in deadlock.
func ( aq * activeQueue ) underLock ( fn func ( unlockedActiveQ unlockedActiveQueuer ) ) {
aq . lock . Lock ( )
defer aq . lock . Unlock ( )
2025-02-25 08:05:33 -05:00
fn ( aq . unlockedQueue )
2024-07-22 06:13:13 -04:00
}
2024-08-20 04:51:21 -04:00
// underLock runs the fn function under the lock.RLock.
2024-08-20 05:21:06 -04:00
// fn can run unlockedActiveQueueReader methods but should NOT run any other activeQueue method,
2024-08-20 04:51:21 -04:00
// as it would end up in deadlock.
2024-08-20 05:21:06 -04:00
func ( aq * activeQueue ) underRLock ( fn func ( unlockedActiveQ unlockedActiveQueueReader ) ) {
2024-08-20 04:51:21 -04:00
aq . lock . RLock ( )
defer aq . lock . RUnlock ( )
2025-02-25 08:05:33 -05:00
fn ( aq . unlockedQueue )
2024-07-22 06:13:13 -04:00
}
2024-08-20 05:21:06 -04:00
// delete deletes the pod info from activeQ.
func ( aq * activeQueue ) delete ( pInfo * framework . QueuedPodInfo ) error {
aq . lock . Lock ( )
defer aq . lock . Unlock ( )
return aq . queue . Delete ( pInfo )
}
2024-07-22 06:13:13 -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.
// It increments scheduling cycle when a pod is popped.
func ( aq * activeQueue ) pop ( logger klog . Logger ) ( * framework . QueuedPodInfo , error ) {
aq . lock . Lock ( )
defer aq . lock . Unlock ( )
2024-08-30 02:37:09 -04:00
return aq . unlockedPop ( logger )
}
func ( aq * activeQueue ) unlockedPop ( logger klog . Logger ) ( * framework . QueuedPodInfo , error ) {
2025-03-10 04:56:21 -04:00
var pInfo * framework . QueuedPodInfo
2024-07-22 06:13:13 -04:00
for aq . queue . Len ( ) == 0 {
2025-03-10 04:56:21 -04:00
// backoffQPopper is non-nil only if SchedulerPopFromBackoffQ feature is enabled.
// In case of non-empty backoffQ, try popping from there.
if aq . backoffQPopper != nil && aq . backoffQPopper . lenBackoff ( ) != 0 {
break
}
2024-07-22 06:13:13 -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 aq . closed {
logger . V ( 2 ) . Info ( "Scheduling queue is closed" )
return nil , nil
}
aq . cond . Wait ( )
}
pInfo , err := aq . queue . Pop ( )
if err != nil {
2025-03-10 04:56:21 -04:00
if aq . backoffQPopper == nil {
return nil , err
}
// Try to pop from backoffQ when activeQ is empty.
pInfo , err = aq . backoffQPopper . popBackoff ( )
if err != nil {
return nil , err
}
metrics . SchedulerQueueIncomingPods . WithLabelValues ( "active" , framework . PopFromBackoffQ ) . Inc ( )
2024-07-22 06:13:13 -04:00
}
pInfo . Attempts ++
// In flight, no concurrent events yet.
if aq . isSchedulingQueueHintEnabled {
2024-08-30 02:37:09 -04:00
// If the pod is already in the map, we shouldn't overwrite the inFlightPods otherwise it'd lead to a memory leak.
// https://github.com/kubernetes/kubernetes/pull/127016
if _ , ok := aq . inFlightPods [ pInfo . Pod . UID ] ; ok {
// Just report it as an error, but no need to stop the scheduler
// because it likely doesn't cause any visible issues from the scheduling perspective.
2025-07-07 21:34:49 -04:00
utilruntime . HandleErrorWithLogger ( logger , nil , "The same pod is tracked in multiple places in the scheduler, and just discard it" , "pod" , klog . KObj ( pInfo . Pod ) )
2024-08-30 02:37:09 -04:00
// Just ignore/discard this duplicated pod and try to pop the next one.
return aq . unlockedPop ( logger )
}
2024-09-04 08:59:27 -04:00
aq . metricsRecorder . ObserveInFlightEventsAsync ( metrics . PodPoppedInFlightEvent , 1 , false )
2024-07-22 06:13:13 -04:00
aq . inFlightPods [ pInfo . Pod . UID ] = aq . inFlightEvents . PushBack ( pInfo . Pod )
}
2024-08-30 02:37:09 -04:00
aq . schedCycle ++
2024-07-22 06:13:13 -04:00
2025-11-24 14:32:41 -05:00
// Update metrics for unschedulable plugins.
2024-07-22 06:13:13 -04:00
for plugin := range pInfo . UnschedulablePlugins . Union ( pInfo . PendingPlugins ) {
metrics . UnschedulableReason ( plugin , pInfo . Pod . Spec . SchedulerName ) . Dec ( )
}
return pInfo , nil
}
// list returns all pods that are in the queue.
func ( aq * activeQueue ) list ( ) [ ] * v1 . Pod {
aq . lock . RLock ( )
defer aq . lock . RUnlock ( )
var result [ ] * v1 . Pod
for _ , pInfo := range aq . queue . List ( ) {
2025-06-11 05:56:18 -04:00
result = append ( result , pInfo . GetPod ( ) )
2024-07-22 06:13:13 -04:00
}
return result
}
// len returns length of the queue.
func ( aq * activeQueue ) len ( ) int {
return aq . queue . Len ( )
}
// has inform if pInfo exists in the queue.
func ( aq * activeQueue ) has ( pInfo * framework . QueuedPodInfo ) bool {
aq . lock . RLock ( )
defer aq . lock . RUnlock ( )
return aq . queue . Has ( pInfo )
}
// listInFlightEvents returns all inFlightEvents.
func ( aq * activeQueue ) listInFlightEvents ( ) [ ] interface { } {
aq . lock . RLock ( )
defer aq . lock . RUnlock ( )
var values [ ] interface { }
for event := aq . inFlightEvents . Front ( ) ; event != nil ; event = event . Next ( ) {
values = append ( values , event . Value )
}
return values
}
// listInFlightPods returns all inFlightPods.
func ( aq * activeQueue ) listInFlightPods ( ) [ ] * v1 . Pod {
aq . lock . RLock ( )
defer aq . lock . RUnlock ( )
var pods [ ] * v1 . Pod
for _ , obj := range aq . inFlightPods {
pods = append ( pods , obj . Value . ( * v1 . Pod ) )
}
return pods
}
// clusterEventsForPod gets all cluster events that have happened during pod for pInfo is being scheduled.
func ( aq * activeQueue ) clusterEventsForPod ( logger klog . Logger , pInfo * framework . QueuedPodInfo ) ( [ ] * clusterEvent , error ) {
aq . lock . RLock ( )
defer aq . lock . RUnlock ( )
logger . V ( 5 ) . Info ( "Checking events for in-flight pod" , "pod" , klog . KObj ( pInfo . Pod ) , "unschedulablePlugins" , pInfo . UnschedulablePlugins , "inFlightEventsSize" , aq . inFlightEvents . Len ( ) , "inFlightPodsSize" , len ( aq . 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 := aq . inFlightPods [ pInfo . Pod . UID ]
if ! ok {
return nil , fmt . Errorf ( "in flight Pod isn't found in the scheduling queue. If you see this error log, it's likely a bug in the scheduler" )
}
var events [ ] * clusterEvent
for event := inFlightPod . Next ( ) ; event != nil ; event = event . Next ( ) {
e , ok := event . Value . ( * clusterEvent )
if ! ok {
// Must be another in-flight Pod (*v1.Pod). Can be ignored.
continue
}
events = append ( events , e )
}
return events , nil
}
2025-08-06 03:35:46 -04:00
// addEventsIfPodInFlight adds events to inFlightEvents if the newPod is in inFlightPods.
2024-07-22 06:13:13 -04:00
// It returns true if pushed the event to the inFlightEvents.
2025-06-26 11:06:29 -04:00
func ( aq * activeQueue ) addEventsIfPodInFlight ( oldPod , newPod * v1 . Pod , events [ ] fwk . ClusterEvent ) bool {
2024-07-22 06:13:13 -04:00
aq . lock . Lock ( )
defer aq . lock . Unlock ( )
2025-08-06 03:35:46 -04:00
return aq . unlockedQueue . addEventsIfPodInFlight ( oldPod , newPod , events )
2024-07-22 06:13:13 -04:00
}
// addEventIfAnyInFlight adds clusterEvent to inFlightEvents if any pod is in inFlightPods.
// It returns true if pushed the event to the inFlightEvents.
2025-06-26 11:06:29 -04:00
func ( aq * activeQueue ) addEventIfAnyInFlight ( oldObj , newObj interface { } , event fwk . ClusterEvent ) bool {
2024-07-22 06:13:13 -04:00
aq . lock . Lock ( )
defer aq . lock . Unlock ( )
if len ( aq . inFlightPods ) != 0 {
2024-10-01 23:24:03 -04:00
aq . metricsRecorder . ObserveInFlightEventsAsync ( event . Label ( ) , 1 , false )
2024-07-22 06:13:13 -04:00
aq . inFlightEvents . PushBack ( & clusterEvent {
event : event ,
oldObj : oldObj ,
newObj : newObj ,
} )
return true
}
return false
}
func ( aq * activeQueue ) schedulingCycle ( ) int64 {
aq . lock . RLock ( )
defer aq . lock . RUnlock ( )
return aq . schedCycle
}
// done must be called for pod returned by Pop. This allows the queue to
// keep track of which pods are currently being processed.
func ( aq * activeQueue ) done ( pod types . UID ) {
aq . lock . Lock ( )
defer aq . lock . Unlock ( )
2025-03-18 05:18:48 -04:00
aq . unlockedDone ( pod )
}
// unlockedDone is used by the activeQueue internally and doesn't take the lock itself.
// It assumes the lock is already taken outside before the method is called.
func ( aq * activeQueue ) unlockedDone ( pod types . UID ) {
2024-07-22 06:13:13 -04:00
inFlightPod , ok := aq . inFlightPods [ pod ]
if ! ok {
// This Pod is already done()ed.
return
}
delete ( aq . inFlightPods , pod )
// Remove the pod from the list.
aq . inFlightEvents . Remove ( inFlightPod )
2024-09-01 11:54:00 -04:00
aggrMetricsCounter := map [ string ] int { }
2024-07-22 06:13:13 -04:00
// 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 := aq . inFlightEvents . Front ( )
if e == nil {
// Empty list.
break
}
2024-09-01 11:54:00 -04:00
ev , ok := e . Value . ( * clusterEvent )
if ! ok {
2024-07-22 06:13:13 -04:00
// A pod, must stop pruning.
break
}
aq . inFlightEvents . Remove ( e )
2024-10-01 23:24:03 -04:00
aggrMetricsCounter [ ev . event . Label ( ) ] --
2024-09-01 11:54:00 -04:00
}
for evLabel , count := range aggrMetricsCounter {
2024-09-04 08:59:27 -04:00
aq . metricsRecorder . ObserveInFlightEventsAsync ( evLabel , float64 ( count ) , false )
2024-07-22 06:13:13 -04:00
}
2024-09-04 08:59:27 -04:00
aq . metricsRecorder . ObserveInFlightEventsAsync ( metrics . PodPoppedInFlightEvent , - 1 ,
// If it's the last Pod in inFlightPods, we should force-flush the metrics.
// Otherwise, especially in small clusters, which don't get a new Pod frequently,
// the metrics might not be flushed for a long time.
len ( aq . inFlightPods ) == 0 )
2024-07-22 06:13:13 -04:00
}
// close closes the activeQueue.
func ( aq * activeQueue ) close ( ) {
2025-03-18 05:18:48 -04:00
aq . lock . Lock ( )
defer aq . lock . Unlock ( )
2024-09-05 23:57:42 -04:00
// We should call done() for all in-flight pods to clean up the inFlightEvents metrics.
// It's safe even if the binding cycle running asynchronously calls done() afterwards
// done() will just be a no-op.
for pod := range aq . inFlightPods {
2025-03-18 05:18:48 -04:00
aq . unlockedDone ( pod )
2024-09-05 23:57:42 -04:00
}
2024-07-22 06:13:13 -04:00
aq . closed = true
}
// broadcast notifies the pop() operation that new pod(s) was added to the activeQueue.
func ( aq * activeQueue ) broadcast ( ) {
aq . cond . Broadcast ( )
}