lens @ 5bb3d3ecabfc658035e69d5d5286cabaf44e3f73

  1package worker
  2
  3import (
  4	"context"
  5	"errors"
  6	"sync"
  7
  8	"github.com/sirupsen/logrus"
  9)
 10
 11type (
 12
 13	// A simple worker to deal with list.
 14	ChanProcessor[T any] interface {
 15		Query(context.Context) (<-chan T, error)
 16		Process(context.Context, T) error
 17	}
 18
 19	ListProcessor[T any] interface {
 20		Query(context.Context) ([]T, error)
 21		Process(context.Context, T) error
 22	}
 23
 24	chanProcessorWorker[T any] struct {
 25		chanProcessor ChanProcessor[T]
 26		scheduler     *Scheduler
 27	}
 28
 29	listProcessorWorker[T any] struct {
 30		listProcessor ListProcessor[T]
 31		logrus        *logrus.Entry
 32		scheduler     *Scheduler
 33	}
 34)
 35
 36func NewWorkerFromListProcessor[T any](
 37	listProcessor ListProcessor[T],
 38	scheduler *Scheduler,
 39	logrus *logrus.Entry,
 40) Worker {
 41	return &listProcessorWorker[T]{
 42		listProcessor: listProcessor,
 43		scheduler:     scheduler,
 44		logrus:        logrus,
 45	}
 46}
 47
 48func NewWorkerFromChanProcessor[T any](
 49	listProcessor ChanProcessor[T],
 50	scheduler *Scheduler,
 51) Worker {
 52	return &chanProcessorWorker[T]{
 53		chanProcessor: listProcessor,
 54		scheduler:     scheduler,
 55	}
 56}
 57
 58func (l *listProcessorWorker[T]) Start(ctx context.Context) error {
 59	for {
 60		values, err := l.listProcessor.Query(ctx)
 61		if err != nil {
 62			return err
 63		}
 64
 65		select {
 66		case <-ctx.Done():
 67			return ctx.Err()
 68		default:
 69		}
 70
 71		if len(values) == 0 {
 72			return nil
 73		}
 74		var wg sync.WaitGroup
 75
 76		for _, v := range values {
 77			wg.Add(1)
 78			l.scheduler.Take()
 79			go func(v T) {
 80				defer l.scheduler.Return()
 81				defer wg.Done()
 82				if err := l.listProcessor.Process(ctx, v); err != nil && !errors.Is(err, context.Canceled) {
 83					l.logrus.WithError(err).Error("Error processing batch")
 84				}
 85			}(v)
 86		}
 87
 88		wg.Wait()
 89	}
 90}
 91
 92func (l *chanProcessorWorker[T]) Start(ctx context.Context) error {
 93	c, err := l.chanProcessor.Query(ctx)
 94	if err != nil {
 95		return err
 96	}
 97
 98	for {
 99		select {
100		case <-ctx.Done():
101			return ctx.Err()
102		case v, ok := <-c:
103			if !ok {
104				return nil
105			}
106
107			if err := l.chanProcessor.Process(ctx, v); err != nil {
108				return err
109			}
110		}
111	}
112}