Help us shape the future of Go! Take the 2026 Go Developer Survey

schedule

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 28, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package schedule persists cron jobs that fire an adh command on a cadence and fires the ones that are due (SPEC-ADDITIONS §15, §18). A job names a command (e.g. `sleep run`, `loop run dep-scan`) and a cron expression; `tick` runs every due job, records its outcome, and advances its next fire time. The store is SQLite so a schedule survives across the stateless-per-invocation CLI; time enters as a parameter so the disposition is deterministic and testable.

Index

Constants

View Source
const DefaultDBFile = "schedule.db"

DefaultDBFile is the schedule database, kept beside the rest of the .adh state.

Variables

View Source
var (
	// ErrNotFound means a named job does not exist.
	ErrNotFound = errors.New("schedule: job not found")
	// ErrConflict means a job with that name already exists.
	ErrConflict = errors.New("schedule: job already exists")
	// ErrInvalidCron means a cron expression was not accepted.
	ErrInvalidCron = errors.New("schedule: invalid cron expression")
	// ErrInvalidSpec means a job spec violates a creation invariant.
	ErrInvalidSpec = errors.New("schedule: invalid job spec")
)

Sentinel errors are part of the package contract; callers branch with errors.Is and command shells map them to exit codes.

Functions

func NextFire

func NextFire(expr string, now time.Time) time.Time

NextFire returns the next time the cron expression fires after now, or the zero time when the expression does not parse (a stored job with a bad expression simply never fires again rather than erroring the whole tick).

func NextSleep

func NextSleep(next, now time.Time, poll time.Duration) time.Duration

NextSleep is how long a daemon should sleep after a tick: until the earlier of the next deadline and the poll cap, floored above zero. The poll cap bounds how long an out-of-band add or remove goes unnoticed (there is no wake channel); a zero next (nothing scheduled) sleeps the full cap. It is pure so the loop's timing is tested without a clock.

func ParseCron

func ParseCron(expr string) (cron.Schedule, error)

ParseCron validates a cron expression and returns a schedule that computes next-fire times. Parser errors wrap ErrInvalidCron; an @every duration must be positive (robfig rounds sub-second values up to a second, so `@every 0s` is pre-checked to stay a visible user error).

Types

type Fired

type Fired struct {
	Name   string    `json:"name"`
	Status RunStatus `json:"status"`
}

Fired records one job the tick ran and its outcome.

func Tick

func Tick(ctx context.Context, store *Store, now time.Time, runner Runner) ([]Fired, error)

Tick runs every job due at now: it executes the command, records the outcome, and advances the job's next fire to the next cron instant after now. One job's failure marks it failed and does not stop the others — the tick reports every job it fired. A store write failure (recording the outcome) is returned, since it means the schedule state is no longer trustworthy.

type Job

type Job struct {
	Name       string    `json:"name"`
	Cron       string    `json:"cron"`
	Command    []string  `json:"command"`
	NextFire   time.Time `json:"next_fire,omitzero"`
	LastRun    time.Time `json:"last_run,omitzero"`
	LastStatus RunStatus `json:"last_status,omitempty"`
	CreatedAt  time.Time `json:"created_at"`
	UpdatedAt  time.Time `json:"updated_at"`
}

Job is a stored schedule entry: a named command fired on a cron cadence, with the denormalized outcome of its last run.

type JobSpec

type JobSpec struct {
	Name    string
	Cron    string
	Command []string
}

JobSpec is the user-supplied subset used to create a job; the store fills in the timestamps and the first NextFire.

func (JobSpec) Validate

func (s JobSpec) Validate() error

Validate enforces the JobSpec invariants required before insert.

type RunStatus

type RunStatus string

RunStatus is the outcome of a job's last run.

const (
	RunOK   RunStatus = "ok"   // the command exited zero
	RunFail RunStatus = "fail" // the command failed to start or exited non-zero
)

RunStatus values (see RunStatus).

type Runner

type Runner interface {
	Run(ctx context.Context, command []string) error
}

Runner runs one job's command and reports whether it succeeded. A nil error is success (the command exited zero); any error — a non-zero exit or a failure to start — marks the run failed. It is the point-of-use seam: the sleep command supplies a runner that execs the adh binary; a test injects a fake.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store persists schedule jobs in SQLite. It is the imperative shell; the cron disposition (ParseCron/NextFire) is the pure core the caller supplies results from. Concurrent callers against one database are serialized by SQLite's WAL and busy timeout.

func Open

func Open(ctx context.Context, dir string) (*Store, error)

Open returns a Store backed by dir/schedule.db, creating dir and applying the schema. ctx scopes the schema apply, not the Store's lifetime; close with Close.

func (*Store) Add

func (s *Store) Add(ctx context.Context, spec JobSpec, now time.Time) (Job, error)

Add inserts a new job from spec, computing its first fire time from now. A name already in use is ErrConflict; an invalid spec is ErrInvalidSpec/ErrInvalidCron.

func (*Store) AdvanceNextFire

func (s *Store) AdvanceNextFire(ctx context.Context, name string, next time.Time) error

AdvanceNextFire sets the job's next fire time; a zero next means it never fires again. A name that does not exist is ErrNotFound.

func (*Store) Close

func (s *Store) Close() error

Close releases the database handle.

func (*Store) Due

func (s *Store) Due(ctx context.Context, now time.Time) ([]Job, error)

Due returns the jobs whose next fire is at or before now, earliest first.

func (*Store) List

func (s *Store) List(ctx context.Context) ([]Job, error)

List returns every job ordered by name.

func (*Store) MarkRan

func (s *Store) MarkRan(ctx context.Context, name string, status RunStatus, at time.Time) error

MarkRan records the outcome of a job's last run.

func (*Store) Remove

func (s *Store) Remove(ctx context.Context, name string) error

Remove deletes the named job; a name that does not exist is ErrNotFound.

func (*Store) SoonestDeadline

func (s *Store) SoonestDeadline(ctx context.Context, now time.Time) (time.Time, error)

SoonestDeadline returns the earliest future fire time after now, or the zero time when nothing is scheduled. It is the seam a future blocking daemon sleeps on; the tick command does not use it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL