package logic import ( "context" "sync" "time" "go.uber.org/zap" ) // Worker runs Tick on an interval until Stop. type Worker struct { log *zap.Logger svc *Service stop chan struct{} wg sync.WaitGroup } func NewWorker(log *zap.Logger, svc *Service) *Worker { if log == nil { log = zap.NewNop() } return &Worker{log: log, svc: svc, stop: make(chan struct{})} } func (w *Worker) Start() { if w == nil || w.svc == nil || !w.svc.Enabled() { return } w.wg.Add(1) go w.loop() } func (w *Worker) Stop() { if w == nil { return } select { case <-w.stop: default: close(w.stop) } w.wg.Wait() } func (w *Worker) loop() { defer w.wg.Done() interval := w.svc.Interval() // Run once shortly after boot so ops can verify without waiting a full interval. timer := time.NewTimer(15 * time.Second) defer timer.Stop() for { select { case <-w.stop: return case <-timer.C: w.runOnce() timer.Reset(interval) } } } func (w *Worker) runOnce() { ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second) defer cancel() if err := w.svc.Tick(ctx); err != nil { w.log.Warn("squarebot tick", zap.Error(err)) } }