Beispiel #1
0
// AllMetas retrieves the metadata for all work specs.  This is
// expected to run within a pre-existing transaction.  On success,
// returns maps from work spec name to work spec object and to
// metadata object.
func (ns *namespace) allMetas(tx *sql.Tx, withCounts bool) (map[string]*workSpec, map[string]*coordinate.WorkSpecMeta, error) {
	query := buildSelect([]string{
		workSpecID,
		workSpecName,
		workSpecPriority,
		workSpecWeight,
		workSpecPaused,
		workSpecContinuous,
		workSpecCanBeContinuous,
		workSpecMinMemoryGb,
		workSpecInterval,
		workSpecNextContinuous,
		workSpecMaxRunning,
		workSpecMaxAttemptsReturned,
		workSpecNextWorkSpec,
	}, []string{
		workSpecTable,
	}, []string{
		inThisNamespace,
	})
	rows, err := tx.Query(query, ns.id)
	if err != nil {
		return nil, nil, err
	}
	specs := make(map[string]*workSpec)
	metas := make(map[string]*coordinate.WorkSpecMeta)
	err = scanRows(rows, func() error {
		var (
			spec           workSpec
			meta           coordinate.WorkSpecMeta
			interval       string
			nextContinuous pq.NullTime
			err            error
		)
		err = rows.Scan(&spec.id, &spec.name, &meta.Priority,
			&meta.Weight, &meta.Paused, &meta.Continuous,
			&meta.CanBeContinuous, &meta.MinMemoryGb,
			&interval, &nextContinuous, &meta.MaxRunning,
			&meta.MaxAttemptsReturned,
			&meta.NextWorkSpecName)
		if err != nil {
			return err
		}
		spec.namespace = ns
		meta.NextContinuous = nullTimeToTime(nextContinuous)
		meta.Interval, err = sqlToDuration(interval)
		if err != nil {
			return err
		}
		specs[spec.name] = &spec
		metas[spec.name] = &meta
		return nil
	})
	if err != nil {
		return nil, nil, err
	}
	if withCounts {
		// A single query that selects both "available" and
		// "pending" is hopelessly expensive.  Also, in the
		// only place this is called (in RequestAttempts) we
		// need to know whether or not there are any available
		// attempts, but we don't really care how many there
		// are so long as there are more than zero.
		//
		// Pending:
		query = buildSelect([]string{workSpecName, "COUNT(*)"},
			[]string{workSpecTable, workUnitTable, attemptTable},
			[]string{
				inThisNamespace, // binds $1
				workUnitInSpec,
				attemptThisWorkUnit,
				attemptIsPending,
			})
		query += " GROUP BY " + workSpecName
		rows, err = tx.Query(query, ns.id)
		if err != nil {
			return nil, nil, err
		}
		err = scanRows(rows, func() error {
			var name string
			var count int
			err := rows.Scan(&name, &count)
			if err == nil {
				metas[name].PendingCount = count
			}
			return err
		})

		// Available count (0/1):
		query = buildSelect([]string{"1"},
			[]string{workUnitTable},
			[]string{workUnitInSpec, hasNoAttempt})
		query = buildSelect(
			[]string{
				workSpecName,
				"EXISTS(" + query + ")",
			},
			[]string{workSpecTable},
			[]string{inThisNamespace})
		rows, err = tx.Query(query, ns.id)
		err = scanRows(rows, func() error {
			var name string
			var present bool
			err := rows.Scan(&name, &present)
			if err == nil {
				if present {
					metas[name].AvailableCount = 1
				} else {
					metas[name].AvailableCount = 0
				}
			}
			return err
		})
		if err != nil {
			return nil, nil, err
		}
	}
	return specs, metas, nil
}
Beispiel #2
0
func (spec *workSpec) Meta(withCounts bool) (coordinate.WorkSpecMeta, error) {
	// If we need counts, we need to run expiry so that the
	// available/pending counts are rightish
	if withCounts {
		_ = withTx(spec, func(tx *sql.Tx) error {
			return expireAttempts(spec, tx)
		})
	}
	var meta coordinate.WorkSpecMeta
	err := withTx(spec, func(tx *sql.Tx) error {
		var (
			query          string
			interval       string
			nextContinuous pq.NullTime
		)
		query = buildSelect([]string{
			workSpecPriority,
			workSpecWeight,
			workSpecPaused,
			workSpecContinuous,
			workSpecCanBeContinuous,
			workSpecMinMemoryGb,
			workSpecInterval,
			workSpecNextContinuous,
			workSpecMaxRunning,
			workSpecMaxAttemptsReturned,
			workSpecNextWorkSpec,
		}, []string{
			workSpecTable,
		}, []string{
			isWorkSpec, // binds $1
		})
		row := tx.QueryRow(query, spec.id)
		err := row.Scan(
			&meta.Priority,
			&meta.Weight,
			&meta.Paused,
			&meta.Continuous,
			&meta.CanBeContinuous,
			&meta.MinMemoryGb,
			&interval,
			&nextContinuous,
			&meta.MaxRunning,
			&meta.MaxAttemptsReturned,
			&meta.NextWorkSpecName,
		)
		if err != nil {
			return err
		}
		meta.NextContinuous = nullTimeToTime(nextContinuous)
		meta.Interval, err = sqlToDuration(interval)
		if err != nil {
			return err
		}

		// Find counts with a second query, if requested
		if !withCounts {
			return nil
		}
		query = buildSelect([]string{
			attemptStatus,
			"COUNT(*)",
		}, []string{
			workUnitAttemptJoin,
		}, []string{
			inThisWorkSpec, // binds $1
		})
		query += " GROUP BY " + attemptStatus
		rows, err := tx.Query(query, spec.id)
		if err != nil {
			return err
		}
		return scanRows(rows, func() error {
			var status sql.NullString
			var count int
			err := rows.Scan(&status, &count)
			if err != nil {
				return err
			}
			if !status.Valid {
				meta.AvailableCount += count
			} else {
				switch status.String {
				case "expired":
					meta.AvailableCount += count
				case "retryable":
					meta.AvailableCount += count
				case "pending":
					meta.PendingCount += count
				}
			}
			return nil
		})
	})
	return meta, err
}