Cron Jobs
VEF exposes github.com/coldsmirk/vef-framework-go/cron as a typed wrapper around gocron. The framework DI module provides cron.Scheduler automatically; applications that need custom wiring can pass their own gocron.Scheduler to cron.NewScheduler.
This page covers the in-memory scheduler: jobs defined in code, scheduled
per process, gone on restart. For database-persisted schedules with
cluster-wide single fire, misfire policies, a run journal, and management
APIs, see Durable Schedules — both live in the same cron
package and coexist in one application.
Public Surface
This section lists the in-memory scheduler's public API only. The same cron
package also exports the durable-scheduling surface (Schedule, Run,
TriggerSpec, ScheduleManager, JobHandler, run events, and their
exported fields) — that half is documented in
Durable Schedules.
| API | Contract |
|---|---|
cron.Scheduler | high-level scheduler interface used by DI, custom handlers, and modules |
cron.NewScheduler(scheduler gocron.Scheduler) cron.Scheduler | wraps a caller-provided gocron.Scheduler; the caller must pass a usable scheduler |
cron.Job | interface returned by Scheduler.NewJob, Scheduler.Jobs, and Scheduler.Update |
cron.JobDefinition | interface accepted by Scheduler.NewJob and Scheduler.Update |
cron.JobDescriptorOption | option type accepted by all built-in job-definition constructors |
cron.OneTimeJobDefinition | one-time job definition returned by NewOneTimeJob |
cron.DurationJobDefinition | fixed-interval job definition returned by NewDurationJob |
cron.DurationRandomJobDefinition | random-interval job definition returned by NewDurationRandomJob |
cron.CronJobDefinition | cron-expression job definition returned by NewCronJob |
cron.NewOneTimeJob(times []time.Time, options ...cron.JobDescriptorOption) *cron.OneTimeJobDefinition | runs immediately when times is nil/empty, once at one time, or once at each supplied time |
cron.NewDurationJob(interval time.Duration, options ...cron.JobDescriptorOption) *cron.DurationJobDefinition | repeats at a fixed duration interval |
cron.NewDurationRandomJob(minInterval time.Duration, maxInterval time.Duration, options ...cron.JobDescriptorOption) *cron.DurationRandomJobDefinition | repeats with a random interval between minInterval and maxInterval |
cron.NewCronJob(expression string, withSeconds bool, options ...cron.JobDescriptorOption) *cron.CronJobDefinition | uses a cron expression; withSeconds=true expects the seconds field, false uses the standard 5-field form |
cron.WithName(name string) | sets the required human-readable job name |
cron.WithTags(tags ...string) | assigns tags used by RemoveByTags and Job.Tags |
cron.WithConcurrent() | allows overlapping executions of the same job; without it, jobs use singleton wait mode |
cron.WithStartAt(startAt time.Time) | starts the schedule at a specific time; this takes precedence over WithStartImmediately |
cron.WithStartImmediately() | starts the schedule immediately when no WithStartAt time is set |
cron.WithStopAt(stopAt time.Time) | stops the schedule at a specific time |
cron.WithLimitedRuns(limitedRuns uint) | applies a run limit only when limitedRuns > 0 |
cron.WithContext(ctx context.Context) | forwards a non-nil context to the underlying job option for cancellation support |
cron.WithTask(handler any, params ...any) | sets the required function handler and forwards params to it through gocron.NewTask |
cron.ErrJobNameRequired | job options were built without a name |
cron.ErrJobTaskHandlerRequired | task build found a nil handler |
cron.ErrJobTaskHandlerMustFunc | task build found a non-function handler |
cron.Scheduler
The public scheduler interface includes:
| Method | Signature | Contract |
|---|---|---|
Jobs | Jobs() []cron.Job | returns the jobs currently registered with the wrapped scheduler |
NewJob | NewJob(definition cron.JobDefinition) (cron.Job, error) | builds the definition, registers it, and returns a cron.Job; validation errors from the definition are returned before registration |
RemoveByTags | RemoveByTags(tags ...string) | removes all jobs that have any of the supplied tags |
RemoveJob | RemoveJob(id string) error | parses id as a UUID and removes that job; invalid non-UUID strings return a parse error before delegation |
Start | Start() | starts scheduling and execution; jobs added after start are scheduled by the wrapped scheduler |
StopJobs | StopJobs() error | stops job execution without removing definitions; jobs can run again after Start() |
Update | Update(id string, definition cron.JobDefinition) (cron.Job, error) | parses id as a UUID, builds the replacement definition, and preserves the job identifier |
JobsWaitingInQueue | JobsWaitingInQueue() int | returns the wrapped scheduler's waiting queue count; it is meaningful when wait mode is in use |
Use IDs returned by Job.ID() for RemoveJob and Update. Arbitrary strings are not accepted.
cron.Job
Each registered job can be inspected or triggered through:
| Method | Signature | Contract |
|---|---|---|
ID | ID() string | returns the underlying UUID as a string |
LastRun | LastRun() (time.Time, error) | returns the last run start time |
Name | Name() string | returns the configured job name |
NextRun | NextRun() (time.Time, error) | returns the next scheduled run time |
NextRuns | NextRuns(count int) ([]time.Time, error) | returns count future scheduled run times |
RunNow | RunNow() error | triggers an immediate execution while respecting job/scheduler limits and run limits |
Tags | Tags() []string | returns the configured tag list |
Job Definitions
cron.JobDefinition is the common interface accepted by Scheduler.NewJob(...) and Scheduler.Update(...). The four exported definition structs are returned by their constructors; callers usually do not instantiate them directly because their scheduling fields are internal.
| Constructor | Schedule |
|---|---|
cron.NewOneTimeJob(nil, options...) or cron.NewOneTimeJob([]time.Time{}, options...) | one immediate run |
cron.NewOneTimeJob([]time.Time{t}, options...) | one run at t |
cron.NewOneTimeJob([]time.Time{a, b}, options...) | one run at each supplied time |
cron.NewDurationJob(interval, options...) | fixed interval |
cron.NewDurationRandomJob(minInterval, maxInterval, options...) | random interval in the supplied range |
cron.NewCronJob(expression, true, options...) | cron expression with seconds |
cron.NewCronJob(expression, false, options...) | standard 5-field cron expression |
All built-in definitions first build the task and then build job options. That means a nil or non-function task handler takes precedence over a missing name if both are invalid.
Job Options
Every constructor accepts JobDescriptorOption values:
| Option | Behavior |
|---|---|
cron.WithName(name) | required; without it NewJob/Update return an error wrapping ErrJobNameRequired |
cron.WithTask(handler, params...) | required; handler must be a function; params are forwarded to gocron.NewTask(handler, params...) |
cron.WithTags(tags...) | stores tags for listing and RemoveByTags |
cron.WithConcurrent() | disables the default singleton wait mode for this job |
cron.WithStartAt(startAt) | sets a start date/time and wins over WithStartImmediately when both are present |
cron.WithStartImmediately() | applies only when WithStartAt did not set a non-zero time |
cron.WithStopAt(stopAt) | adds a stop date/time only when stopAt is non-zero |
cron.WithLimitedRuns(limitedRuns) | adds a run limit only when limitedRuns > 0 |
cron.WithContext(ctx) | adds the context option only when ctx is non-nil; handlers that accept context.Context can observe cancellation |
By default, a job cannot overlap with another execution of itself. The package adds singleton wait mode unless WithConcurrent() is supplied.
Error Sentinels
| Error | Trigger |
|---|---|
cron.ErrJobTaskHandlerRequired | WithTask was not supplied or received nil |
cron.ErrJobTaskHandlerMustFunc | WithTask received a non-function handler |
cron.ErrJobNameRequired | task build succeeded, but WithName was missing or empty |
These sentinels are wrapped by the build path, for example failed to build job task: ... or failed to build job options: .... Use errors.Is(err, cron.ErrJobNameRequired) and the other sentinels instead of direct equality.
DI Scheduler Defaults
When the framework creates the scheduler through its DI module, the observable defaults are:
| Default | Meaning |
|---|---|
| local time zone | schedules use time.Local |
stop timeout 30s | graceful shutdown window |
| scheduler logger and monitor | job scheduling and completion are logged |
concurrent job limit 1000 with wait mode | excess executions wait in the queue instead of being dropped |
| lifecycle start/shutdown | app start calls scheduler start; app stop shuts the scheduler down |
These defaults apply to the framework-provided scheduler. A scheduler passed to cron.NewScheduler uses whatever options the caller configured.
Minimal Example
package jobs
import (
"context"
"time"
"github.com/coldsmirk/vef-framework-go/cron"
)
func RegisterCleanupJob(scheduler cron.Scheduler) error {
_, err := scheduler.NewJob(
cron.NewDurationJob(
10*time.Minute,
cron.WithName("cleanup-expired-sessions"),
cron.WithTask(func(ctx context.Context) error {
return nil
}),
),
)
return err
}
Practical Usage
Use the in-memory scheduler when you need application-owned background work such as:
- periodic cleanup
- polling and sync tasks
- scheduled aggregation
- retry or timeout scanning
If the task is domain-heavy, keep business logic in a service and use the scheduler only for orchestration.
Prefer Durable Schedules instead when the schedule must survive restarts, run exactly once across replicas, be editable by operators, or leave an auditable run journal.
Next Step
Read Durable Schedules for the persistent scheduling engine, or Transactions if scheduled work needs explicit database transaction control.