func Claim(ctx scope.Context, jq JobQueue, handlerID string, pollTime time.Duration, stealChance float64) (*Job, error) { for ctx.Err() == nil { if rand.Float64() < stealChance { job, err := jq.TrySteal(ctx, handlerID) if err != nil && err != ErrJobNotFound { return nil, err } if err == nil { return job, nil } } job, err := jq.TryClaim(ctx, handlerID) if err != nil { if err == ErrJobNotFound { child := ctx.ForkWithTimeout(pollTime) if err = jq.WaitForJob(child); err != nil && err != scope.TimedOut { return nil, err } continue } return nil, err } return job, nil } return nil, ctx.Err() }
func (c *Controller) processOne(ctx scope.Context) error { job, err := c.claimOrSteal(ctx.ForkWithTimeout(StatsInterval)) if err != nil { if err == scope.TimedOut { return nil } return err } if job.Type != jobs.EmailJobType { return jobs.ErrInvalidJobType } payload, err := job.Payload() if err != nil { return err } return job.Exec(ctx, func(ctx scope.Context) error { labels := prometheus.Labels{"queue": c.jq.Name()} defer processedCounter.With(labels).Inc() if err := c.w.Work(ctx, job, payload); err != nil { failedCounter.With(labels).Inc() return err } completedCounter.With(labels).Inc() return nil }) }
func (j *Job) Exec(ctx scope.Context, f func(scope.Context) error) error { if j.JobClaim == nil { return ErrJobNotClaimed } w := io.MultiWriter(os.Stdout, j) prefix := fmt.Sprintf("[%s-%s] ", j.Queue.Name(), j.HandlerID) deadline := time.Now().Add(j.MaxWorkDuration) child := logging.LoggingContext(ctx.ForkWithTimeout(j.MaxWorkDuration), w, prefix) if err := f(child); err != nil { if err != scope.TimedOut { delay := time.Duration(j.AttemptsMade+1) * BackoffDuration if time.Now().Add(delay).After(deadline) { delay = deadline.Sub(time.Now()) } time.Sleep(delay) } if ferr := j.Fail(ctx, err.Error()); ferr != nil { return ferr } return err } if err := j.Complete(ctx); err != nil { return err } return nil }