Files
triggerdotdev--trigger.dev/docs/v3/tasks-scheduled.mdx
T
Matt Aitken c405ae7117 Schedule limits and timezone support (#1165)
* Added maximumScheduleInstancesLimit column to Org, default to 20

* Docs on the schedule limits and improved soft-limit communication

* Added limit info to the schedules list page

* Created a task that creates schedules, useful for testing

* Make deduplicationKey required when creating/updating a schedule using the SDK

* New schedule button shows an alert if you’re over the limit

* Added timezone to the form and db

* WIP on the timezone dropdown for the create/edit schedule form

* Use the new filter search for timezones

* Made the timezone dropdown faster by fixing the virtualization

* The preview table is working and added a nice message about daylight savings

* Created a page where you can view the full list of timezones

The URL is included in the error message if you send an invalid time using the SDK

* Creating tasks with the timezone

* Added timezone support the the scheduler and the schedules list

* Added timezone support to more of the schedules UI

* The timezone comes through to scheduled runs with nice JSDocs

* Allow setting the timezone from the SDK

* Always have a timezone on a schedule

* Updated jsdocs

* Updated catalog example

* Changed the column to be a string, not null. Added the timezone across the SDK

* API endpoint for getting the timezones

* Added an SDK function to get the list of timezones

* Added timezones to the docs

* Changeset: Added timezone support to schedules

* Added support for testing timezone

* Tidied up imports

* Imports

* Imports

* Update limits.mdx

* Fixed a couple type issues and use the already exported zodfetch

---------

Co-authored-by: Eric Allam <eallam@icloud.com>
2024-06-14 13:31:27 +01:00

307 lines
11 KiB
Plaintext

---
title: "Scheduled tasks"
description: "A task that is triggered on a recurring schedule using CRON syntax."
---
To use scheduled tasks you need to do two things:
1. Define a task in your code using `schedules.task()`.
2. Attach a schedule to the task either using the dashboard or the SDK.
<Info>A task can have multiple schedules attached to it.</Info>
Like all tasks they don't have timeouts, they should be placed inside a [/trigger folder](/v3/trigger-folder), and you [can configure them](/v3/tasks-overview#defining-a-task).
## Defining a scheduled task
```ts
import { schedules } from "@trigger.dev/sdk/v3";
//this task will run when any of the attached schedules trigger
export const firstScheduledTask = schedules.task({
id: "first-scheduled-task",
run: async (payload) => {
//when the task was scheduled to run
//note this will be slightly different from new Date() because it takes a few ms to run the task
console.log(payload.timestamp); //is a Date object
//when the task was last run
//this can be undefined if it's never been run
console.log(payload.lastTimestamp); //is a Date object or undefined
//the timezone the schedule was registered with, defaults to "UTC"
//this is in IANA format, e.g. "America/New_York"
//See the full list here: https://cloud.trigger.dev/timezones
console.log(payload.timezone); //is a string
//If you want to output the time in the user's timezone do this:
const formatted = payload.timestamp.toLocaleString("en-US", {
timeZone: payload.timezone,
});
//the schedule id (you can have many schedules for the same task)
//using this you can remove the schedule, update it, etc
console.log(payload.scheduleId); //is a string
//you can optionally provide an external id when creating the schedule
//usually you would set this to a userId or some other unique identifier
//this can be undefined if you didn't provide one
console.log(payload.externalId); //is a string or undefined
//the next 5 dates this task is scheduled to run
console.log(payload.upcoming); //is an array of Date objects
},
});
```
You can see from the comments that the payload has several useful properties:
- `timestamp` - the time the task was scheduled to run, as a UTC date.
- `lastTimestamp` - the time the task was last run, as a UTC date.
- `timezone` - the timezone the schedule was registered with, defaults to "UTC". In IANA format, e.g. "America/New_York".
- `scheduleId` - the id of the schedule that triggered the task
- `externalId` - the external id you (optionally) provided when creating the schedule
- `upcoming` - the next 5 times the task is scheduled to run
<Note>
This task will NOT get triggered on a schedule until you attach a schedule to it. Read on for how
to do that.
</Note>
## Supported CRON syntax
```
* * * * *
┬ ┬ ┬ ┬ ┬
│ │ │ │ |
│ │ │ │ └ day of week (0 - 7, 1L - 7L) (0 or 7 is Sun)
│ │ │ └───── month (1 - 12)
│ │ └────────── day of month (1 - 31, L)
│ └─────────────── hour (0 - 23)
└──────────────────── minute (0 - 59)
```
"L" means the last. In the "day of week" field, 1L means the last Monday of the month. In the "day of month" field, L means the last day of the month.
We do not support seconds in the CRON syntax.
## When schedules won't trigger
There are two situations when a scheduled task won't trigger:
- For Dev environments scheduled tasks will only trigger if you're running the dev CLI.
- For Staging/Production environments scheduled tasks will only trigger if the task is in the current deployment (latest version). We won't trigger tasks from previous deployments.
## Attaching schedules in the dashboard
You need to attach a schedule to a task before it will run on a schedule. You can attach static schedules in the dashboard:
<Steps>
<Step title="Go to the Schedules page">
In the sidebar select the "Schedules" page, then press the "New schedule" button. Or you can
follow the onboarding and press the create in dashboard button. ![Blank schedules
page](/images/v3/schedules-blank.png)
</Step>
<Step title="Create your schedule">
Fill in the form and press "Create schedule" when you're done. ![Environment variables
page](/images/v3/schedules-create.png)
These are the options when creating a schedule:
| Name | Description |
| ----------------- | --------------------------------------------------------------------------------------------- |
| Task | The id of the task you want to attach to. |
| CRON pattern | The schedule in CRON format. |
| Timezone | The timezone the schedule will run in. Defaults to "UTC" |
| External id | An optional external id, usually you'd use a userId. |
| Deduplication key | An optional deduplication key. If you pass the same value, it will update rather than create. |
| Environments | The environments this schedule will run in. |
</Step>
</Steps>
## Attaching schedules with the SDK
You call `schedules.create()` to create a schedule from your code. Here's the simplest possible example:
```ts
const createdSchedule = await schedules.create({
//The id of the scheduled task you want to attach to.
task: firstScheduledTask.id,
//The schedule in CRON format.
cron: "0 0 * * *",
//this is required, it prevents you from creating duplicate schedules. It will update the schedule if it already exists.
deduplicationKey: "my-deduplication-key",
});
```
<Note>The `task` id must be a task that you defined using `schedules.task()`.</Note>
You can create many schedules with the same `task`, `cron`, and `externalId` but only one with the same `deduplicationKey`.
This means you can have thousands of schedules attached to a single task, but only one schedule per `deduplicationKey`. Here's an example with all the options:
```ts
const createdSchedule = await schedules.create({
//The id of the scheduled task you want to attach to.
task: firstScheduledTask.id,
//The schedule in CRON format.
cron: "0 0 * * *",
// Optional, it defaults to "UTC". In IANA format, e.g. "America/New_York".
// In this case, the task will run at midnight every day in New York time.
// If you specify a timezone it will automatically work with daylight saving time.
timezone: "America/New_York",
//Optionally, you can specify your own IDs (like a user ID) and then use it inside the run function of your task.
//This allows you to have per-user CRON tasks.
externalId: "user_123456",
//You can only create one schedule with this key.
//If you use it twice, the second call will update the schedule.
//This is useful because you don't want to create duplicate schedules for a user.
deduplicationKey: "user_123456-todo_reminder",
});
```
See [the SDK reference](/v3/management/schedules/create) for full details.
### Dynamic schedules (or multi-tenant schedules)
By using the `externalId` you can have schedules for your users. This is useful for things like reminders, where you want to have a schedule for each user.
A reminder task:
```ts /trigger/reminder.ts
import { schedules } from "@trigger.dev/sdk/v3";
//this task will run when any of the attached schedules trigger
export const reminderTask = schedules.task({
id: "todo-reminder",
run: async (payload) => {
if (!payload.externalId) {
throw new Error("externalId is required");
}
//get user using the externalId you used when creating the schedule
const user = await db.getUser(payload.externalId);
//send a reminder email
await sendReminderEmail(user);
},
});
```
Then in your backend code, you can create a schedule for each user:
```ts Next.js API route
import { reminderTask } from "~/trigger/reminder";
//app/reminders/route.ts
export async function POST(request: Request) {
//get the JSON from the request
const data = await request.json();
//create a schedule for the user
const createdSchedule = await schedules.create({
task: reminderTask.id,
//8am every day
cron: "0 8 * * *",
//the user's timezone
timezone: data.timezone,
//the user id
externalId: data.userId,
//this makes it impossible to have two reminder schedules for the same user
deduplicationKey: `${data.userId}-reminder`,
});
//return a success response with the schedule
return Response.json(createdSchedule);
}
```
You can also retrieve, list, delete, deactivate and re-activate schedules using the SDK. More on that later.
## Testing schedules
You can test a scheduled task in the dashboard. Note that the `scheduleId` will always come through as `sched_1234` to the run.
<Steps>
<Step title="Go to the Test page">
In the sidebar select the "Test" page, then select a scheduled task from the list (they have a
clock icon on them) ![Test page](/images/v3/schedules-test.png)
</Step>
<Step title="Create your schedule">
Fill in the form [1]. You can select from a recent run [2] to pre-populate the fields. Press "Run
test" when you're ready ![Schedule test form](/images/v3/schedules-test-form.png)
</Step>
</Steps>
## Managing schedules with the SDK
### Retrieving an existing schedule
```ts
const retrievedSchedule = await schedules.retrieve(scheduleId);
```
See [the SDK reference](/v3/management/schedules/retrieve) for full details.
### Listing schedules
```ts
const allSchedules = await schedules.list();
```
See [the SDK reference](/v3/management/schedules/list) for full details.
### Updating a schedule
```ts
const updatedSchedule = await schedules.update(scheduleId, {
task: firstScheduledTask.id,
cron: "0 0 1 * *",
externalId: "ext_1234444",
deduplicationKey: "my-deduplication-key",
});
```
See [the SDK reference](/v3/management/schedules/update) for full details.
### Deactivating a schedule
```ts
const deactivatedSchedule = await schedules.deactivate(scheduleId);
```
See [the SDK reference](/v3/management/schedules/deactivate) for full details.
### Activating a schedule
```ts
const activatedSchedule = await schedules.activate(scheduleId);
```
See [the SDK reference](/v3/management/schedules/activate) for full details.
### Deleting a schedule
```ts
const deletedSchedule = await schedules.del(scheduleId);
```
See [the SDK reference](/v3/management/schedules/delete) for full details.
### Getting possible timezones
You might want to show a dropdown menu in your UI so your users can select their timezone. You can get a list of all possible timezones using the SDK:
```ts
const timezones = await schedules.timezones();
```
See [the SDK reference](/v3/management/schedules/timezones) for full details.