Architecture
This page explains how go-scheduler parses schedules, dispatches tasks, tracks dependencies, and shuts down.
System overview
graph TB
App[Application] --> Cron[cron scheduler]
Cron --> Parser[Cron parser]
Cron --> Heap[Task min-heap]
Cron --> Depend[Dependency subsystem]
Depend --> Workers[Dependency workers]
Heap --> Runner[Task runner]
Workers --> Runner
Runner --> State[Task state update]
Scheduler lifecycle
core.New creates the scheduler and initializes its location, logger, channels, min-heap, parser, and dependency manager. The scheduler uses time.Local when Config.Location is nil. It first attempts to write structured logs to syslog and falls back to stderr when syslog is unavailable.
Start is idempotent while the scheduler is running. It starts the dependency workers, calculates the first occurrence for every task already registered, and enters the event loop. The loop waits for the nearest task in the heap with a timer. When the timer fires, due tasks are removed, dispatched, and scheduled again when another occurrence exists.
Add, Remove, and RemoveAll can be called while the scheduler is running. These operations use channels to update the event loop safely; before Start, they update the heap directly. Stop stops the event loop and dependency workers, then returns a context that is canceled after in-flight scheduler tasks finish.
Scheduling path
Addparses a five-field expression, descriptor, or@everyinterval.- The task receives a monotonically increasing
int64ID and is inserted into the min-heap. Startcomputes the next occurrence in the configured location.- The event loop pops the nearest due task.
- Tasks without dependencies run directly; dependent tasks enter the dependency queue.
- The task result changes the state to completed or failed.
- Recurring tasks receive a new next occurrence and return to the heap.
Dependency subsystem
A dependent task stores a slice of Wait values. Dependency workers call the manager to inspect each prerequisite:
| Prerequisite state | Wait.State |
Dependent behavior |
|---|---|---|
| Completed | Either | Continue checking the remaining prerequisites. |
| Failed | Stop |
Mark the dependency check as failed. |
| Failed | Skip |
Ignore that failed prerequisite and continue. |
| Pending or running | Either | Keep waiting until completion or timeout. |
| Missing | Either | Return a dependency-not-found error. |
The dependency subsystem starts with two workers and expands to runtime.NumCPU() when the host exposes more than two CPUs. Dependency checks use a context timeout; a zero task wait duration uses the one-minute default in the worker queue.
Scheduling data structures
The scheduler uses a taskHeap min-heap ordered by task.next. The event loop therefore only needs to wait for the first item. Parser results implement a common schedule interface. A cron schedule searches forward by minute, while an @every schedule returns the current time plus its fixed duration.
Supported cron field forms are *, a single value, a range, a comma-separated list, and */n. The parser validates each field against its allowed range before a task is accepted.
Execution and failure handling
Each task records pending, running, completed, or failed state. A task action may be func() or func() error; dependency-enabled tasks must return an error so failures can be propagated. A configured timeout runs the action with a deadline and invokes onDelay when the deadline is reached. Returned errors and recovered panics are treated as task failures.
See Core Concepts for scheduling and dependency behavior, and API Reference for callable methods.