func (w *worker) RequestAttempts(req coordinate.AttemptRequest) ([]coordinate.Attempt, error) { var ( attempts []coordinate.Attempt specs map[string]*workSpec metas map[string]*coordinate.WorkSpecMeta name string err error spec *workSpec meta *coordinate.WorkSpecMeta ) // Run system-global expiry. w.Coordinate().Expiry.Do(w) // Collect the set of candidate work specs and metadata outside // the main transaction. This is pretty expensive to collect // and we want to avoid retrying it if possible. // // There is a possible race condition on a bad day. It is // possible that this returns work specs with positive // available units, but while we're deciding what to do, // another worker picks those up. That means the scheduler // could pick something but we then fail to get any work from // it. for { err = withTx(w, true, func(tx *sql.Tx) (err error) { specs, metas, err = w.namespace.allMetas(tx, true) return }) if err != nil { return nil, err } // Now pick something (this is stateless, but see TODO above) // (If this picks nothing, we're done) metas = coordinate.LimitMetasToNames(metas, req.WorkSpecs) metas = coordinate.LimitMetasToRuntimes(metas, req.Runtimes) now := w.Coordinate().clock.Now() name, err = coordinate.SimplifiedScheduler(metas, now, req.AvailableGb) if err == coordinate.ErrNoWork { return attempts, nil } else if err != nil { return nil, err } spec = specs[name] meta = metas[name] // Then get some attempts attempts, err = w.requestAttemptsForSpec(req, spec, meta) if err != nil { return nil, err } // If that returned non-zero attempts, we're done if len(attempts) > 0 { return attempts, nil } // Otherwise reloop } }
func (w *worker) RequestAttempts(req coordinate.AttemptRequest) ([]coordinate.Attempt, error) { globalLock(w) defer globalUnlock(w) var attempts []coordinate.Attempt if req.NumberOfWorkUnits < 1 { req.NumberOfWorkUnits = 1 } // Get the metadata and choose a work spec specs, metas := w.namespace.allMetas(true) metas = coordinate.LimitMetasToNames(metas, req.WorkSpecs) metas = coordinate.LimitMetasToRuntimes(metas, req.Runtimes) now := w.Coordinate().clock.Now() name, err := coordinate.SimplifiedScheduler(metas, now, req.AvailableGb) if err == coordinate.ErrNoWork { return attempts, nil } else if err != nil { return nil, err } spec := specs[name] meta := metas[name] // Get more work units, but not more than either the number // requested or the maximum allowed count := req.NumberOfWorkUnits if meta.MaxAttemptsReturned > 0 && count > meta.MaxAttemptsReturned { count = meta.MaxAttemptsReturned } if meta.MaxRunning > 0 && count > meta.MaxRunning-meta.PendingCount { count = meta.MaxRunning - meta.PendingCount } for len(attempts) < count { attempt := w.getWorkFromSpec(spec, meta) if attempt == nil { break } attempts = append(attempts, attempt) meta.PendingCount++ } return attempts, nil }