Schedules
This page shows how to do the following:
Scheduled Workflows
Scheduling Workflows is a crucial aspect of any automation process, especially when dealing with time-sensitive tasks. By scheduling a Workflow, you can automate repetitive tasks, reduce the need for manual intervention, and ensure timely execution of your business processes.
Use any of the following actions to help Schedule a Workflow Execution and take control over your automation process.
Schedule behavior is governed by the Schedule's Overlap Policy. If a Workflow Execution started by a Schedule is Paused, it remains open and counts as the running execution for overlap decisions.
Create a Schedule
Schedules are initiated with the create call.
The user generates a unique Schedule ID for each new Schedule.
To create a Schedule in Go, use Create() on the Client.
Schedules must be initialized with a Schedule ID, Spec, and Action in client.ScheduleOptions{}.
go-sandbox/workflows/schedules/schedules.go
func CreateSchedule(ctx context.Context, temporalClient client.Client) (client.ScheduleHandle, error) {
scheduleID := "schedule_id"
workflowID := "schedule_workflow_id"
scheduleHandle, err := temporalClient.ScheduleClient().Create(ctx, client.ScheduleOptions{
ID: scheduleID,
Spec: client.ScheduleSpec{},
Action: &client.ScheduleWorkflowAction{
ID: workflowID,
Workflow: ScheduleWorkflow,
TaskQueue: TaskQueue,
},
})
if err != nil {
return nil, fmt.Errorf("unable to create schedule: %w", err)
}
log.Println("Schedule created", "ScheduleID", scheduleID)
return scheduleHandle, nil
}
Once a Schedule has completed creating all its Workflow Executions, the Temporal Service deletes it since it won’t fire again. The Temporal Service doesn't guarantee when this removal will happen.
Backfill a Schedule
Backfilling a Schedule executes Workflow Tasks ahead of the Schedule's specified time range. This is useful for executing a missed or delayed Action, or for testing the Workflow ahead of time.
To backfill a Schedule in Go, use Backfill() on ScheduleHandle.
Specify the start and end times to execute the Workflow, along with the overlap policy.
go-sandbox/workflows/schedules/schedules.go
func BackfillSchedule(ctx context.Context, temporalClient client.Client) error {
workflowID := "schedule_workflow_"
// Create a paused Schedule to backfill
now := time.Now()
scheduleHandle, err := temporalClient.ScheduleClient().Create(ctx, client.ScheduleOptions{
ID: "backfill-schedule",
Spec: client.ScheduleSpec{
Intervals: []client.ScheduleIntervalSpec{
{
Every: time.Minute,
},
},
},
Action: &client.ScheduleWorkflowAction{
ID: workflowID,
Workflow: ScheduleWorkflow,
TaskQueue: TaskQueue,
},
Paused: true,
})
if err != nil {
return fmt.Errorf("unable to create schedule: %w", err)
}
err = scheduleHandle.Backfill(ctx, client.ScheduleBackfillOptions{
Backfill: []client.ScheduleBackfill{
{
Start: now.Add(-4 * time.Minute),
End: now.Add(-2 * time.Minute),
Overlap: enums.SCHEDULE_OVERLAP_POLICY_ALLOW_ALL,
},
{
Start: now.Add(-2 * time.Minute),
End: now,
Overlap: enums.SCHEDULE_OVERLAP_POLICY_ALLOW_ALL,
},
},
})
if err != nil {
return fmt.Errorf("unable to backfill schedule: %w", err)
}
return nil
}
Delete a Schedule
Deleting a Schedule erases a Schedule. Deletion does not affect any Workflows started by the Schedule.
To delete a Schedule, use Delete() on the ScheduleHandle.
go-sandbox/workflows/schedules/schedules.go
func DeleteSchedule(ctx context.Context, temporalClient client.Client) error {
scheduleID := "schedule_id"
// Retrieve the schedule handle by its ID
scheduleHandle := temporalClient.ScheduleClient().GetHandle(ctx, scheduleID)
log.Println("Deleting schedule", "ScheduleID", scheduleHandle.GetID())
err := scheduleHandle.Delete(ctx)
if err != nil {
return fmt.Errorf("unable to delete schedule: %w", err)
}
return nil
}
Describe a Schedule
Describe retrieves information about the current Schedule configuration.
This can include details about the Schedule Spec (such as Intervals), CronExpressions, and Schedule State.
To describe a Schedule, use Describe() on the ScheduleHandle.
go-sandbox/workflows/schedules/schedules.go
func DescribeSchedule(ctx context.Context, temporalClient client.Client) (*client.ScheduleDescription, error) {
// create Schedule
scheduleHandle, err := temporalClient.ScheduleClient().Create(ctx, client.ScheduleOptions{
ID: "test-schedule-describe-spec-cron-schedule",
Spec: client.ScheduleSpec{
CronExpressions: []string{
"0 12 * * MON",
},
},
Action: &client.ScheduleWorkflowAction{
ID: "schedule_workflow_describe",
Workflow: ScheduleWorkflow,
TaskQueue: TaskQueue,
},
})
if err != nil {
return nil, fmt.Errorf("unable to create schedule: %w", err)
}
// describe schedule
description, err := scheduleHandle.Describe(ctx)
if err != nil {
return nil, fmt.Errorf("unable to describe schedule: %w", err)
}
log.Println("Schedule next action times", description.Info.NextActionTimes)
return description, nil
}
List Schedules
The List action returns all available Schedules and their respective Schedule IDs.
To return information on all Schedules, use ScheduleClient.List().
go-sandbox/workflows/schedules/schedules.go
func ListSchedules(ctx context.Context, temporalClient client.Client) error {
// Create Schedule and Workflow IDs
scheduleID := "schedule_" + uuid.NewString()
workflowID := "schedule_workflow_" + uuid.NewString()
// Create the schedule.
scheduleHandle, err := temporalClient.ScheduleClient().Create(ctx, client.ScheduleOptions{
ID: scheduleID,
Spec: client.ScheduleSpec{},
Action: &client.ScheduleWorkflowAction{
ID: workflowID,
Workflow: ScheduleWorkflow,
TaskQueue: TaskQueue,
},
})
if err != nil {
return fmt.Errorf("unable to create schedule: %w", err)
}
log.Println("Schedule created", "ScheduleID", scheduleHandle.GetID())
// list schedules
listView, err := temporalClient.ScheduleClient().List(ctx, client.ScheduleListOptions{
PageSize: 1,
})
if err != nil {
return fmt.Errorf("unable to list schedules: %w", err)
}
for listView.HasNext() {
entry, err := listView.Next()
if err != nil {
return fmt.Errorf("unable to get next schedule: %w", err)
}
log.Println("Schedule", "ScheduleID", entry.ID)
}
return nil
}
Pause a Schedule
Pause and Unpause enable the start or stop of all future Workflow Runs on a given Schedule.
Pausing a Schedule halts all future Workflow Runs.
Pausing can be enabled by setting State.Paused to true, or by using Pause() on the ScheduleHandle.
Unpausing a Schedule allows the Workflow to execute as planned.
To unpause a Schedule, use Unpause() on ScheduleHandle.
go-sandbox/workflows/schedules/schedules.go
func PauseSchedule(ctx context.Context, temporalClient client.Client) error {
scheduleID := "schedule_id"
scheduleHandle := temporalClient.ScheduleClient().GetHandle(ctx, scheduleID)
// Pause the schedule and print the status
err := scheduleHandle.Pause(ctx, client.SchedulePauseOptions{
Note: "The Schedule has been paused.",
})
if err != nil {
return fmt.Errorf("unable to pause schedule: %w", err)
}
log.Println("The Schedule has been paused.")
// Wait for 5 seconds
time.Sleep(5 * time.Second)
// Unpause the schedule
err = scheduleHandle.Unpause(ctx, client.ScheduleUnpauseOptions{
Note: "The Schedule has been unpaused.",
})
if err != nil {
return fmt.Errorf("unable to unpause schedule: %w", err)
}
log.Println("The Schedule has been unpaused.")
return nil
}
Trigger a Schedule
Triggering a Schedule immediately executes an Action defined in that Schedule.
By default, trigger is subject to the Overlap Policy.
To trigger a Scheduled Workflow Execution, use Trigger() on ScheduleHandle.
go-sandbox/workflows/schedules/schedules.go
func TriggerSchedule(ctx context.Context, temporalClient client.Client) error {
scheduleHandle, err := temporalClient.ScheduleClient().Create(ctx, client.ScheduleOptions{
ID: "trigger-schedule",
Spec: client.ScheduleSpec{},
Action: &client.ScheduleWorkflowAction{
ID: "trigger-schedule-workflow",
Workflow: ScheduleWorkflow,
TaskQueue: TaskQueue,
},
Paused: true,
Overlap: enums.SCHEDULE_OVERLAP_POLICY_ALLOW_ALL,
})
if err != nil {
return fmt.Errorf("unable to create schedule: %w", err)
}
// Trigger Schedule
for i := 0; i < 5; i++ {
err = scheduleHandle.Trigger(ctx, client.ScheduleTriggerOptions{
Overlap: enums.SCHEDULE_OVERLAP_POLICY_ALLOW_ALL,
})
if err != nil {
return fmt.Errorf("unable to trigger schedule: %w", err)
}
time.Sleep(2 * time.Second)
}
return nil
}
Update a Schedule
Updating a Schedule changes the configuration of an existing Schedule. These changes can be made to Workflow Actions, Action parameters, Memos, and the Workflow's Cancellation Policy.
Use Update() on the ScheduleHandle to modify a Schedule.
The DoUpdate function receives the current Schedule description and returns the Schedule to save.
go-sandbox/workflows/schedules/schedules.go
func UpdateSchedule(ctx context.Context, temporalClient client.Client) error {
scheduleHandle, err := temporalClient.ScheduleClient().Create(ctx, client.ScheduleOptions{
ID: "update-schedule",
Spec: client.ScheduleSpec{},
Action: &client.ScheduleWorkflowAction{
ID: "update-schedule-workflow",
Workflow: ScheduleWorkflow,
TaskQueue: TaskQueue,
},
Paused: true,
})
if err != nil {
return fmt.Errorf("unable to create schedule: %w", err)
}
updateSchedule := func(input client.ScheduleUpdateInput) (*client.ScheduleUpdate, error) {
// Change the Schedule to run every hour.
input.Description.Schedule.Spec = &client.ScheduleSpec{
Intervals: []client.ScheduleIntervalSpec{{Every: time.Hour}},
}
return &client.ScheduleUpdate{
Schedule: &input.Description.Schedule,
}, nil
}
err = scheduleHandle.Update(ctx, client.ScheduleUpdateOptions{
DoUpdate: updateSchedule,
})
if err != nil {
return fmt.Errorf("unable to update schedule: %w", err)
}
return nil
}
Start Delay
Use StartDelay to schedule a Workflow Execution at a specific one-time future point rather than on a recurring schedule.
Create an instance of StartWorkflowOptions from the go.temporal.io/sdk/client package, set the StartDelay field, and pass the instance to the ExecuteWorkflow call.
go-sandbox/workflows/schedules/start.go
workflowOptions := client.StartWorkflowOptions{
ID: "your-workflow-id",
TaskQueue: TaskQueue,
// Start the workflow in 12 hours
StartDelay: 12 * time.Hour,
}
workflowRun, err := temporalClient.ExecuteWorkflow(context.Background(), workflowOptions, YourWorkflowDefinition)
if err != nil {
return nil, err
}
Temporal Cron Jobs
We recommend using Schedules instead of Cron Jobs. Schedules were built to provide a better developer experience, including more configuration options and the ability to update or pause running Schedules.
A Temporal Cron Job is the series of Workflow Executions that occur when a Cron Schedule is provided in the call to spawn a Workflow Execution.
A Cron Schedule is provided as an option when the call to spawn a Workflow Execution is made.
Create an instance of StartWorkflowOptions from the go.temporal.io/sdk/client package, set the CronSchedule field, and pass the instance to the ExecuteWorkflow call.
- Type:
string - Default: None
go-sandbox/workflows/schedules/start.go
workflowOptions := client.StartWorkflowOptions{
ID: "your-cron-workflow-id",
TaskQueue: TaskQueue,
CronSchedule: "15 8 * * *",
}
workflowRun, err := temporalClient.ExecuteWorkflow(context.Background(), workflowOptions, YourWorkflowDefinition)
if err != nil {
return nil, err
}
Temporal Workflow Schedule Cron strings follow this format:
┌───────────── minute (0 - 59)
│ ┌───────────── hour (0 - 23)
│ │ ┌───────────── day of the month (1 - 31)
│ │ │ ┌───────────── month (1 - 12)
│ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday)
│ │ │ │ │
* * * * *