December 18, 2023

How do I convert my Cron into a Schedule?

Patrick Rachford, Quinn Klassen & Irina Belova

Patrick Rachford, Quinn Klassen & Irina Belova

Overview

Temporal Schedules are a replacement for traditional cron jobs for task scheduling because this Schedules provide a more durable way to execute tasks, allow insight into their progress, enable observability of schedules and workflow runs, and let you start, stop, and pause them.

Now that Temporal Schedules are GA, it’s time to convert those cron jobs into Schedules. In this article we will step through the process and show some examples.

By converting to Schedules, you can immediately take advantage of the benefits which include:

  • Enhanced workflow control and visibility
  • Flexible and extensible scheduling
  • Elimination of external dependencies

For more information about Schedules, check out this blog post.

Converting to Schedules

Thankfully the process of converting to Schedules is very simple. The Temporal SDKs provide new primitives for Schedule CRUD operations. In addition to the SDKs, the CLI and UI are also able to perform Schedule CRUD operations. First, setup and test your new Temporal Schedule using the interface of your choice. Then once your Schedule is working and you are comfortable, turn off your old cron job. That is it!

For more information, you can refer to the Temporal documentation.

Typescript Example

import { Connection, Client } from '@temporalio/client';
import { temporalCommunityWorkflow } from './workflows';

async function run() {
  const client = new Client({
    connection: await Connection.connect(),
  });

  // https://typescript.temporal.io/api/classes/client.ScheduleClient#create
  await client.schedule.create({
    action: {
      type: 'startWorkflow',
      workflowType: temporalCommunityWorkflow,
      workflowId: "temporal-community-workflow",
      taskQueue: 'schedules-task-queue',
    },
    scheduleId: 'top-stories-every-mon-at-noon',
    spec: {
      cronExpressions: ['0 12 * * MON'],
    },
  });

  await client.connection.close();
}

run().catch((err) => {
  console.error(err);
  process.exit(1);
});

See also the Schedules sample application.

Go Example

Dev guide ▶️ Go ▶️ Features ▶️ Schedules

package main

import (
	"context"
	"log"

	"example.com/myworkflows"
	"go.temporal.io/sdk/client"
)

func main() {
	ctx := context.Background()
	// The client is a heavyweight object that should be created once per process.
	c, err := client.Dial(client.Options{
		HostPort: client.DefaultHostPort,
	})
	if err != nil {
		log.Fatalln("Unable to create client", err)
	}
	defer c.Close()

	_, err := c.ScheduleClient().Create(ctx, client.ScheduleOptions{
		ID: "top-stories-every-mon-at-noon",
		Spec: client.ScheduleSpec{
			CronExpressions: []string{"0 12 * * MON"},
		},
		Action: &client.ScheduleWorkflowAction{
			ID:        "temporal-community-workflow",
			TaskQueue: "schedules-task-queue",
			Workflow:  myworkflows.SampleScheduleWorkflow,
		},
	})
	if err != nil {
		log.Fatalln("Unable to create schedule", err)
	}
}

Java Example

Dev guide ▶️ Java ▶️ Features ▶️ Schedules

package io.temporal.samples.hello;

import io.temporal.client.WorkflowOptions;
import io.temporal.client.schedules.*;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.serviceclient.WorkflowServiceStubsOptions;
import java.util.Collections;

public class HelloSchedules {
  public static void main(String[] args) {
    // Get a Workflow service stub.
    WorkflowServiceStubs service = WorkflowServiceStubs.newServiceStubs(WorkflowServiceStubsOptions.newBuilder().setTarget("localhost:7233").build());

    /*
     * Get a Schedule client which can be used to interact with schedule.
     */
    ScheduleClient scheduleClient = ScheduleClient.newInstance(service);

    /*
     * Create the workflow options for our schedule.
     * Note: Not all workflow options are supported for schedules.
     */
    WorkflowOptions workflowOptions =
        WorkflowOptions.newBuilder().setWorkflowId("temporal-community-workflow").setTaskQueue("schedules-task-queue").build();

    /*
     * Create the action that will be run when the schedule is triggered.
     */
    ScheduleActionStartWorkflow action =
        ScheduleActionStartWorkflow.newBuilder()
            .setWorkflowType(HelloActivity.GreetingWorkflow.class)
            .setOptions(workflowOptions)
            .build();

    // Define the schedule spec
    ScheduleSpec spec = ScheduleSpec.newBuilder().setCronExpressions(Collections.singletonList("0 12 * * MON")).build();

    // Define the schedule we want to create
    Schedule schedule =
        Schedule.newBuilder().setAction(action).setSpec(spec).build();

    // Create a schedule on the server
     scheduleClient.createSchedule("top-stories-every-mon-at-noon", schedule, ScheduleOptions.newBuilder().build());  }
}

.NET Example

using Temporalio.Client;
using Temporalio.Client.Schedules;
using MyWorkflows;;

// Create a client to localhost on default namespace
var client = await TemporalClient.ConnectAsync(new("localhost:7233"));

var action = ScheduleActionStartWorkflow.Create<MyWorkflow>(
    wf => wf.RunAsync(),
    new()
    {
        Id = "temporal-community-workflow",
        TaskQueue = "schedules-task-queue",
    });

var spec = new ScheduleSpec
{
    CronExpressions = new List<string> { "0 12 * * MON" },
};

var schedule = new Schedule(action, spec) { };
await client.CreateScheduleAsync("top-stories-every-mon-at-noon", schedule); 

See also the Schedules sample application.

Python Example

Dev guide ▶️ Python ▶️ Features ▶️ Schedules

To convert your cron job into a Schedule using Python with Temporal, you can use the create_schedule() function provided by Temporal's Python SDK. Here's a step-by-step guide:

  1. First, create a new file, for example, schedule_workflow.py.
  2. Import the necessary modules and your workflow. Here's an example:
import asyncio  
from datetime import timedelta  

from temporalio.client import (  
    Client,  
    Schedule,  
    ScheduleActionStartWorkflow,  
    ScheduleIntervalSpec,  
    ScheduleSpec,  
    ScheduleState,  
)  

from activities import TASK_QUEUE_NAME  
from your_workflow import TemporalCommunityWorkflow
  1. Define your main function and connect to the Temporal client:
async def main():  
    client = await Client.connect("localhost:7233")
  1. Use the create_schedule() function on the Client and pass a unique identifier for the Schedule. This identifier can be a business process identifier, for example, temporal-community-workflow. It's crucial for each Schedule to have a unique identifier to avoid conflicts and ensure clear identification.
  2. Use the Schedule class on the Client to set the Schedule action and spec. The Schedule provides a solution to running your actions periodically. The spec determines when the action is taken.
  3. In create_schedule, set an cron expression, for example, 0 12 * * MON. While this tutorial uses a cron expression, you can set an interval, calendars, and more to run your Workflow.

Here's an example of how to do this:

async def main():
    client = await Client.connect("localhost:7233")

    await client.create_schedule(
        "top-stories-every-mon-at-noon",
        Schedule(
            action=ScheduleActionStartWorkflow(
                TemporalCommunityWorkflow.run,
                id="temporal-community-workflow",
                task_queue="schedules-task-queue",
            ),
	    	cron_expression=["0 12 * * MON"],
    )
  1. Finally, run the following command to start the Schedule:
python schedule_workflow.py

Additional resources: