github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/process/queue.go (about)

     1  package process
     2  
     3  import (
     4  	"runtime/debug"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/sirupsen/logrus"
     9  	"k8s.io/apimachinery/pkg/util/wait"
    10  	"k8s.io/client-go/util/workqueue"
    11  )
    12  
    13  type Executor interface {
    14  	Execute(operationID string) (time.Duration, error)
    15  }
    16  
    17  type Queue struct {
    18  	queue     workqueue.RateLimitingInterface
    19  	executor  Executor
    20  	waitGroup sync.WaitGroup
    21  	log       logrus.FieldLogger
    22  
    23  	speedFactor int64
    24  }
    25  
    26  func NewQueue(executor Executor, log logrus.FieldLogger) *Queue {
    27  	return &Queue{
    28  		queue:     workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "operations"),
    29  		executor:  executor,
    30  		waitGroup: sync.WaitGroup{},
    31  		log:       log,
    32  
    33  		speedFactor: 1,
    34  	}
    35  }
    36  
    37  func (q *Queue) Add(processId string) {
    38  	q.queue.Add(processId)
    39  }
    40  
    41  func (q *Queue) AddAfter(processId string, duration time.Duration) {
    42  	q.queue.AddAfter(processId, duration)
    43  }
    44  
    45  func (q *Queue) ShutDown() {
    46  	q.queue.ShutDown()
    47  }
    48  
    49  func (q *Queue) Run(stop <-chan struct{}, workersAmount int) {
    50  	for i := 0; i < workersAmount; i++ {
    51  		q.waitGroup.Add(1)
    52  		q.createWorker(q.queue, q.executor.Execute, stop, &q.waitGroup, q.log)
    53  	}
    54  }
    55  
    56  // SpeedUp changes speedFactor parameter to reduce time between processing operations.
    57  // This method should only be used for testing purposes
    58  func (q *Queue) SpeedUp(speedFactor int64) {
    59  	q.speedFactor = speedFactor
    60  }
    61  
    62  func (q *Queue) createWorker(queue workqueue.RateLimitingInterface, process func(id string) (time.Duration, error), stopCh <-chan struct{}, waitGroup *sync.WaitGroup, log logrus.FieldLogger) {
    63  	go func() {
    64  		wait.Until(q.worker(queue, process, log), time.Second, stopCh)
    65  		waitGroup.Done()
    66  	}()
    67  }
    68  
    69  func (q *Queue) worker(queue workqueue.RateLimitingInterface, process func(key string) (time.Duration, error), log logrus.FieldLogger) func() {
    70  	return func() {
    71  		exit := false
    72  		for !exit {
    73  			exit = func() bool {
    74  				key, shutdown := queue.Get()
    75  				if shutdown {
    76  					return true
    77  				}
    78  				id := key.(string)
    79  				log = log.WithField("operationID", id)
    80  				defer func() {
    81  					if err := recover(); err != nil {
    82  						log.Errorf("panic error from process: %v. Stacktrace: %s", err, debug.Stack())
    83  					}
    84  					queue.Done(key)
    85  				}()
    86  
    87  				when, err := process(id)
    88  				if err == nil && when != 0 {
    89  					log.Infof("Adding %q item after %s", id, when)
    90  					afterDuration := time.Duration(int64(when) / q.speedFactor)
    91  					queue.AddAfter(key, afterDuration)
    92  					return false
    93  				}
    94  				if err != nil {
    95  					log.Errorf("Error from process: %v", err)
    96  				}
    97  
    98  				queue.Forget(key)
    99  				return false
   100  			}()
   101  		}
   102  	}
   103  }