Improved readyPod queue implementation and bugfix (#1890)

Co-authored-by: Vishal <vishal-biyani@users.noreply.github.com>
This commit is contained in:
Rahul Bhati
2021-02-02 17:11:54 +05:30
committed by GitHub
co-authored by Vishal
parent 19adf0cd7e
commit 3b4ca09931
2 changed files with 36 additions and 39 deletions
+31 -35
View File
@@ -68,7 +68,7 @@ type (
stopReadyPodControllerCh chan struct{} stopReadyPodControllerCh chan struct{}
readyPodController cache.Controller readyPodController cache.Controller
readyPodIndexer cache.Indexer readyPodIndexer cache.Indexer
readyPodQueue workqueue.RateLimitingInterface readyPodQueue workqueue.DelayingInterface
poolInstanceID string // small random string to uniquify pod names poolInstanceID string // small random string to uniquify pod names
instanceID string // poolmgr instance id instanceID string // poolmgr instance id
} }
@@ -165,6 +165,7 @@ func (gp *GenericPool) getDeployAnnotations() map[string]string {
// returns the key and pod API object. // returns the key and pod API object.
func (gp *GenericPool) choosePod(newLabels map[string]string) (string, *apiv1.Pod, error) { func (gp *GenericPool) choosePod(newLabels map[string]string) (string, *apiv1.Pod, error) {
startTime := time.Now() startTime := time.Now()
expoDelay := 100 * time.Millisecond
for { for {
// Retries took too long, error out. // Retries took too long, error out.
if time.Since(startTime) > gp.podReadyTimeout { if time.Since(startTime) > gp.podReadyTimeout {
@@ -175,36 +176,34 @@ func (gp *GenericPool) choosePod(newLabels map[string]string) (string, *apiv1.Po
var chosenPod *apiv1.Pod var chosenPod *apiv1.Pod
var key string var key string
if gp.readyPodQueue.Len() > 0 { item, quit := gp.readyPodQueue.Get()
item, quit := gp.readyPodQueue.Get() if quit {
if quit { gp.logger.Error("readypod controller is not running")
gp.logger.Error("readypod controller is not running") return "", nil, errors.New("readypod controller is not running")
return "", nil, errors.New("readypod controller is not running") }
} key = item.(string)
gp.logger.Debug("got key from the queue", zap.String("key", key))
key := item.(string) obj, exists, err := gp.readyPodIndexer.GetByKey(key)
obj, exists, err := gp.readyPodIndexer.GetByKey(key) if err != nil {
if err != nil { gp.logger.Error("fetching object from store failed", zap.String("key", key), zap.Error(err))
gp.logger.Error("fetching object from store failed", zap.String("key", key), zap.Error(err)) return "", nil, err
return "", nil, err }
}
if !exists { if !exists {
gp.logger.Warn("pod deleted from store", zap.String("pod", key)) gp.logger.Warn("pod deleted from store", zap.String("pod", key))
continue
}
if !utils.IsReadyPod(obj.(*apiv1.Pod)) {
continue
}
chosenPod = obj.(*apiv1.Pod).DeepCopy()
} else {
// Wait for pods to get ready and retry
gp.logger.Info("waiting for ready pods")
time.Sleep(1000 * time.Millisecond)
continue continue
} }
if !utils.IsReadyPod(obj.(*apiv1.Pod)) {
gp.logger.Warn("pod not ready, pod will be checked again", zap.String("key", key), zap.Duration("delay", expoDelay))
gp.readyPodQueue.Done(key)
gp.readyPodQueue.AddAfter(key, expoDelay)
expoDelay *= 2
continue
}
chosenPod = obj.(*apiv1.Pod).DeepCopy()
if gp.env.Spec.AllowedFunctionsPerContainer != fv1.AllowedFunctionsPerContainerInfinite { if gp.env.Spec.AllowedFunctionsPerContainer != fv1.AllowedFunctionsPerContainerInfinite {
// Relabel. If the pod already got picked and // Relabel. If the pod already got picked and
// modified, this should fail; in that case just // modified, this should fail; in that case just
@@ -220,7 +219,10 @@ func (gp *GenericPool) choosePod(newLabels map[string]string) (string, *apiv1.Po
gp.logger.Info("relabel pod", zap.String("pod", patch)) gp.logger.Info("relabel pod", zap.String("pod", patch))
newPod, err := gp.kubernetesClient.CoreV1().Pods(chosenPod.Namespace).Patch(chosenPod.Name, k8sTypes.StrategicMergePatchType, []byte(patch)) newPod, err := gp.kubernetesClient.CoreV1().Pods(chosenPod.Namespace).Patch(chosenPod.Name, k8sTypes.StrategicMergePatchType, []byte(patch))
if err != nil { if err != nil {
gp.logger.Error("failed to relabel pod", zap.Error(err), zap.String("pod", chosenPod.Name)) gp.logger.Error("failed to relabel pod", zap.Error(err), zap.String("pod", chosenPod.Name), zap.Duration("delay", expoDelay))
gp.readyPodQueue.Done(key)
gp.readyPodQueue.AddAfter(key, expoDelay)
expoDelay *= 2
continue continue
} }
@@ -556,19 +558,13 @@ func (gp *GenericPool) getFuncSvc(ctx context.Context, fn *fv1.Function) (*fscac
if err != nil { if err != nil {
return nil, err return nil, err
} }
gp.readyPodQueue.Done(key)
defer gp.readyPodQueue.Done(key)
err = gp.specializePod(ctx, pod, fn) err = gp.specializePod(ctx, pod, fn)
if err != nil { if err != nil {
if gp.readyPodQueue.NumRequeues(key) < 5 {
gp.readyPodQueue.AddRateLimited(key)
} else {
gp.readyPodQueue.Forget(key)
}
gp.scheduleDeletePod(pod.ObjectMeta.Name) gp.scheduleDeletePod(pod.ObjectMeta.Name)
return nil, err return nil, err
} }
gp.logger.Info("specialized pod", zap.String("pod", pod.ObjectMeta.Name), zap.Any("function", fn.ObjectMeta)) gp.logger.Info("specialized pod", zap.String("pod", pod.ObjectMeta.Name), zap.Any("function", fn.ObjectMeta))
var svcHost string var svcHost string
@@ -1,6 +1,8 @@
package poolmgr package poolmgr
import ( import (
"time"
"go.uber.org/zap" "go.uber.org/zap"
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -12,7 +14,7 @@ import (
func (gp *GenericPool) startReadyPodController() { func (gp *GenericPool) startReadyPodController() {
// create the pod watcher to filter by labels // create the pod watcher to filter by labels
// Filtering pod by phase=Running. In some cases the pod can be in // Filtering pod by phase=Running. In some cases the pod can be in
// different sate than Running, for example Kubernetes sets a // different state than Running, for example Kubernetes sets a
// pod to Termination while k8s waits for the grace period of // pod to Termination while k8s waits for the grace period of
// the pod, even if all the containers are in Ready state. // the pod, even if all the containers are in Ready state.
optionsModifier := func(options *metav1.ListOptions) { optionsModifier := func(options *metav1.ListOptions) {
@@ -22,19 +24,18 @@ func (gp *GenericPool) startReadyPodController() {
} }
readyPodWatcher := cache.NewFilteredListWatchFromClient(gp.kubernetesClient.CoreV1().RESTClient(), "pods", gp.namespace, optionsModifier) readyPodWatcher := cache.NewFilteredListWatchFromClient(gp.kubernetesClient.CoreV1().RESTClient(), "pods", gp.namespace, optionsModifier)
gp.readyPodQueue = workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()) gp.readyPodQueue = workqueue.NewDelayingQueue()
gp.readyPodIndexer, gp.readyPodController = cache.NewIndexerInformer(readyPodWatcher, &apiv1.Pod{}, 0, cache.ResourceEventHandlerFuncs{ gp.readyPodIndexer, gp.readyPodController = cache.NewIndexerInformer(readyPodWatcher, &apiv1.Pod{}, 0, cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) { AddFunc: func(obj interface{}) {
key, err := cache.MetaNamespaceKeyFunc(obj) key, err := cache.MetaNamespaceKeyFunc(obj)
if err == nil { if err == nil {
gp.readyPodQueue.Add(key) gp.readyPodQueue.AddAfter(key, 100*time.Millisecond)
gp.logger.Debug("add func called", zap.String("key", key)) gp.logger.Debug("add func called", zap.String("key", key))
} }
}, },
DeleteFunc: func(obj interface{}) { DeleteFunc: func(obj interface{}) {
key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
if err == nil { if err == nil {
gp.readyPodQueue.Forget(key)
gp.readyPodQueue.Done(key) gp.readyPodQueue.Done(key)
gp.logger.Debug("delete func called", zap.String("key", key)) gp.logger.Debug("delete func called", zap.String("key", key))
} }