Example #1
0
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.
	_ = withTx(w, func(tx *sql.Tx) error {
		return expireAttempts(w, tx)
	})

	// 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, 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)
		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
	}
}
Example #2
0
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)
	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
}