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 }
// recoverPanic tries to handle panics in job execution. // A stacktrace is stored into Job last_error. func recoverPanic(j *Job) { if r := recover(); r != nil { // record an error on the job with panic message and stacktrace stackBuf := make([]byte, 1024) n := runtime.Stack(stackBuf, false) buf := &bytes.Buffer{} fmt.Fprintf(buf, "%v\n", r) fmt.Fprintln(buf, string(stackBuf[:n])) fmt.Fprintln(buf, "[...]") stacktrace := buf.String() log.Printf("event=panic job_id=%d job_type=%s\n%s", j.ID, j.Type, stacktrace) if err := j.Error(stacktrace); err != nil { log.Printf("attempting to save error on job %d: %v", j.ID, err) } } }