func (w *Worker) WorkOne() (didWork bool) { j, err := w.c.LockJob(w.Queue) if err != nil { log.Printf("attempting to lock job: %v", err) return } if j == nil { return // no job was available } defer j.Done() defer recoverPanic(j) didWork = true wf, ok := w.m[j.Type] if !ok { msg := fmt.Sprintf("unknown job type: %q", j.Type) log.Println(msg) if err = j.Error(msg); err != nil { log.Printf("attempting to save error on job %d: %v", j.ID, err) } return } if err = wf(j); err != nil { j.Error(err.Error()) return } if err = j.Delete(); err != nil { log.Printf("attempting to delete job %d: %v", j.ID, err) } log.Printf("event=job_worked job_id=%d job_type=%s", j.ID, j.Type) return }
// Shutdown tells the worker to finish processing its current job and then stop. // There is currently no timeout for in-progress jobs. This function blocks // until the Worker has stopped working. It should only be called on an active // Worker. func (w *Worker) Shutdown() { w.mu.Lock() defer w.mu.Unlock() if w.done { return } log.Println("worker shutting down gracefully...") w.ch <- struct{}{} w.done = true close(w.ch) }
// Work pulls jobs off the Worker's Queue at its Interval. This function only // returns after Shutdown() is called, so it should be run in its own goroutine. func (w *Worker) Work() { for { select { case <-w.ch: log.Println("worker done") return case <-time.After(w.Interval): for { if didWork := w.WorkOne(); !didWork { break // didn't do any work, go back to sleep } } } } }