Improved readyPod queue implementation and bugfix (#1890)
Co-authored-by: Vishal <vishal-biyani@users.noreply.github.com>
This commit is contained in:
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user