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 }