cron-jobs

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cron Jobs and Scheduled Tasks

定时任务与计划任务

IMPORTANT: Before doing anything, you MUST read
BASE_SKILL.md
in this skill's directory. It contains essential guidance on debugging, error handling, state management, deployment, and project setup. Those rules and patterns apply to all RivetKit work. Everything below assumes you have already read and understood it.
重要提示:在进行任何操作之前,你必须阅读本技能目录下的
BASE_SKILL.md
文件。其中包含调试、错误处理、状态管理、部署和项目设置的关键指南。这些规则和模式适用于所有RivetKit工作。以下所有内容均假设你已阅读并理解该文件。

Working Examples

实用示例

If you need a reference implementation, read the raw working example code in these templates:
Patterns for running durable cron jobs and scheduled tasks on Rivet Actors. Actor schedules are persistent timers owned by the engine, so a job keeps its deadline through actor sleep, restarts, upgrades, deploys, and crashes.
如果你需要参考实现,请查看以下模板中的完整实用示例代码:
介绍在Rivet Actors上运行持久化定时任务与计划任务的模式。Actor计划任务是由引擎托管的持久化定时器,因此即使Actor处于休眠、重启、升级、部署或崩溃状态,任务仍会在截止时间执行。

Starter Code

初始代码

Start from the working Scheduling example on GitHub. It implements a reminder service with one-shot timers, a React frontend, and live trigger events.
从GitHub上的计划任务示例开始。该示例实现了一个提醒服务,包含一次性定时器、React前端和实时触发事件。

The Scheduling API

计划任务API

The full API is documented in Scheduling. There are two methods, both available on the actor context:
MethodBehavior
c.schedule.after(duration, actionName, ...args)
Runs the named action after
duration
milliseconds.
c.schedule.at(timestamp, actionName, ...args)
Runs the named action at an exact epoch timestamp in milliseconds.
Key properties:
  • Durable: The schedule is persisted by the engine and the timer survives actor sleep, restart, upgrade, and crash. If the actor is asleep at the deadline, the engine wakes it to run the action. See Lifecycle.
  • Plain actions as callbacks: The scheduled callback is an ordinary action on the same actor, invoked by name. Arguments after the action name are forwarded positionally, for example
    c.schedule.after(delayMs, "triggerReminder", reminder.id)
    .
  • No cancellation API: Rivet does not currently support canceling a scheduled action. The pattern is a tombstone guard: remove the entry from state and have the scheduled action no-op when it cannot find its entry. The example's
    cancelReminder
    and
    triggerReminder
    actions implement exactly this.
完整API文档请查看计划任务。Actor上下文提供以下两种方法:
方法行为
c.schedule.after(duration, actionName, ...args)
duration
毫秒后执行指定的action。
c.schedule.at(timestamp, actionName, ...args)
在指定的毫秒级时间戳(epoch)执行指定的action。
核心特性:
  • 持久化:计划任务由引擎持久化存储,定时器可在Actor休眠、重启、升级和崩溃后继续运行。如果截止时间时Actor处于休眠状态,引擎会唤醒它以执行action。详情请查看生命周期
  • 普通Action作为回调:计划任务的回调是同一Actor上的普通action,通过名称调用。Action名称后的参数会按位置传递,例如
    c.schedule.after(delayMs, "triggerReminder", reminder.id)
  • 无取消API:Rivet目前不支持取消已计划的action。解决方案是墓碑守卫模式:从状态中移除条目,当计划的action找不到该条目时则执行空操作。示例中的
    cancelReminder
    triggerReminder
    action正是实现了这种模式。

Recurring Jobs Via Re-Arm

通过重新触发实现重复任务

c.schedule
is one-shot, so recurring jobs are built by having the scheduled action re-arm itself at the end of each run:
typescript
import { actor } from "rivetkit";

const DAY_MS = 24 * 60 * 60 * 1000;

export const dailyReport = actor({
  state: { lastRunAt: 0 },
  actions: {
    runReport: (c) => {
      // Do the job's work, then record the run.
      c.state.lastRunAt = Date.now();
      // Re-arm the next run before returning.
      c.schedule.after(DAY_MS, "runReport");
    },
  },
});
Arm the first run from
onCreate
or a setup action; after that, the action keeps the chain alive by rescheduling itself.
Re-arming with
after
measures the next run from the end of the current one, so the cadence drifts later by the job's runtime on every cycle. If runs must stay aligned to a fixed cadence, re-arm with
c.schedule.at(c.state.lastRunAt + DAY_MS, "runReport")
instead.
The Scheduling example itself only uses one-shot reminders. A real re-arm implementation lives in the idle world actor, where the
collectProduction
action credits production, updates
lastCollectedAt
, and calls a
scheduleCollection
helper that re-arms with
c.schedule.after(delayMs, "collectProduction", { buildingId })
. It also shows catch-up handling: if the action runs late, it computes how many whole intervals elapsed since the last run and credits them in one batch before re-arming.
For multi-step jobs that need retries and progress tracking inside a single run, consider Workflows instead of chaining schedules.
c.schedule
是一次性的,因此重复任务需要让计划的action在每次运行结束时重新触发自身:
typescript
import { actor } from "rivetkit";

const DAY_MS = 24 * 60 * 60 * 1000;

export const dailyReport = actor({
  state: { lastRunAt: 0 },
  actions: {
    runReport: (c) => {
      // Do the job's work, then record the run.
      c.state.lastRunAt = Date.now();
      // Re-arm the next run before returning.
      c.schedule.after(DAY_MS, "runReport");
    },
  },
});
首次运行可通过
onCreate
或初始化action触发;之后,该action会通过重新调度自身来维持任务链的运行。
使用
after
重新触发时,下一次运行的时间是从当前运行结束时开始计算的,因此每次循环的节奏会因任务运行时间而逐渐延后。如果需要严格按照固定节奏运行,请改用
c.schedule.at(c.state.lastRunAt + DAY_MS, "runReport")
来重新触发。
计划任务示例本身仅使用一次性提醒。真正的重新触发实现可参考闲置世界Actor,其中
collectProduction
action会统计产量、更新
lastCollectedAt
,并调用
scheduleCollection
辅助函数,通过
c.schedule.after(delayMs, "collectProduction", { buildingId })
重新触发任务。该示例还展示了补处理:如果action延迟运行,它会计算自上次运行以来经过了多少个完整周期,并在重新触发前批量统计这些周期的产量。
对于需要在单次运行中进行重试和进度跟踪的多步骤任务,建议使用工作流而非链式计划任务。

Durability Comparison

持久化对比

ApproachTimer DurabilityHorizontal ScalingDeploys And Restarts
setTimeout
/
setInterval
In-process memory onlyEvery replica arms its own timer, so jobs run once per instanceAll pending timers are lost on restart or crash
node-cron
and similar libraries
In-process memory onlyEvery instance runs the job unless you add external lockingSchedule resets on deploy; runs missed during downtime are skipped
External cron serviceLives outside your appNeeds a public HTTP endpoint plus its own dedupe and retry stateSurvives your deploys but is separate infrastructure to operate
Rivet Actor schedulingPersisted by the engine as a durable timerExactly one actor per key, so the timer is armed once rather than once per replicaSurvives actor sleep, restart, upgrade, and crash
方案定时器持久化能力水平扩展部署与重启
setTimeout
/
setInterval
仅进程内存存储每个副本都会触发自己的定时器,因此每个实例都会执行一次任务重启或崩溃时所有待处理定时器都会丢失
node-cron
及类似库
仅进程内存存储除非添加外部锁,否则每个实例都会执行任务部署时计划任务会重置;停机期间错过的任务会被跳过
外部定时任务服务独立于应用之外运行需要公开HTTP端点以及自身的去重和重试状态可在应用部署后继续运行,但需要单独维护基础设施
Rivet Actor计划任务由引擎作为持久化定时器存储每个key对应一个Actor,因此定时器仅触发一次,而非每个副本都触发可在Actor休眠、重启、升级和崩溃后继续运行

Idempotency

幂等性

A scheduled action can fire more than you expect: a crash between doing the work and re-arming can cause the action to run again, and because schedules cannot be cancelled, an action can fire for an entry that was already removed. Design handlers so a duplicate firing is harmless:
  • Store a run marker in state: Keep a
    lastRunAt
    timestamp or a sequence number in actor state and update it inside the action. On each firing, compute elapsed time since the marker and skip or batch accordingly. The idle world actor's
    collectProduction
    does this with
    lastCollectedAt
    and whole-interval batching.
  • Tombstone guard for cancelled entries: The example's
    triggerReminder
    looks the reminder up in
    c.state.reminders
    first and returns with a warning if it is gone, so a fire-after-cancel is a safe no-op.
  • Keep work and marker updates in the same action: Actor state writes are persisted with the action, so updating the marker in the same handler that does the work keeps the two consistent.
计划的action可能会超出预期次数触发:在执行任务和重新触发之间发生崩溃可能导致action再次运行;此外,由于无法取消计划任务,已移除的条目对应的action仍可能触发。因此需将处理器设计为重复触发时无副作用:
  • 在状态中存储运行标记:在Actor状态中保存
    lastRunAt
    时间戳或序列号,并在action内部更新它。每次触发时,计算自标记以来经过的时间,然后相应地跳过或批量处理。闲置世界Actor的
    collectProduction
    action正是通过
    lastCollectedAt
    和完整周期批量处理实现了这一点。
  • 已取消条目的墓碑守卫:示例中的
    triggerReminder
    action会先在
    c.state.reminders
    中查找提醒,如果找不到则返回警告,因此取消后触发的action会执行安全的空操作。
  • 将任务执行与标记更新放在同一个action中:Actor状态写入会随action一起持久化,因此在执行任务的同一个处理器中更新标记可确保两者一致性。

Topology

拓扑结构

TopologyUse WhenExample Key
Singleton job actorOne global job such as a nightly report or cleanup pass
job["daily-report"]
Actor per scheduled entityPer-user or per-resource timers such as reminders, trials, or billing periods
reminder[userId]
The Scheduling example uses a single shared
reminderActor["main"]
key for demo simplicity. For production reminder systems, prefer one actor per user so timers, state, and load are isolated per entity. See Keys.
拓扑结构使用场景示例Key
单例任务Actor全局任务,如夜间报告或清理任务
job["daily-report"]
每个计划实体对应一个Actor按用户或资源划分的定时器,如提醒、试用或计费周期
reminder[userId]
计划任务示例为了演示简便,使用了单个共享的
reminderActor["main"]
key。对于生产环境的提醒系统,建议为每个用户分配一个Actor,以便每个实体的定时器、状态和负载相互隔离。详情请查看Keys

Reminder Service Example

提醒服务示例

TopicSummary
SchedulingOne-shot timers armed with
c.schedule.after(delayMs, "triggerReminder", reminder.id)
or
c.schedule.at(timestamp, "triggerReminder", reminder.id)
.
StateJSON state holding
reminders
and
completedCount
; the scheduled action mutates state when it fires.
Events
triggerReminder
broadcasts a
reminderTriggered
event to all connected clients. See Events.
Cancellation
cancelReminder
only removes the reminder from state; the scheduled action may still fire and no-ops via a state lookup guard.
Actors
  • Key:
    reminderActor["main"]
  • Responsibility: Stores reminders in persistent state, arms a future self-action per reminder via
    c.schedule
    , marks reminders completed when the scheduled action fires, and broadcasts
    reminderTriggered
    to connected clients.
  • Actions
    • scheduleReminder
    • scheduleReminderAt
    • triggerReminder
    • getReminders
    • cancelReminder
    • getStats
  • Queues
    • None
  • State
    • JSON
    • reminders
    • completedCount
Lifecycle
mermaid
sequenceDiagram
	participant C as Client
	participant R as reminderActor
	participant E as Engine

	C->>R: scheduleReminder(message, delayMs)
	R->>E: schedule.after(delayMs, triggerReminder, id)
	Note over E: schedule persisted + alarm armed
	R-->>C: reminder
	Note over R: actor sleeps
	E->>R: alarm fires, actor wakes
	Note over R: triggerReminder(id) runs
	R-->>C: reminderTriggered event
	Note over R: recurring jobs re-arm here with schedule.after
主题概述
计划任务使用
c.schedule.after(delayMs, "triggerReminder", reminder.id)
c.schedule.at(timestamp, "triggerReminder", reminder.id)
触发一次性定时器。
状态JSON状态包含
reminders
completedCount
;计划的action触发时会修改状态。
事件
triggerReminder
会向所有连接的客户端广播
reminderTriggered
事件。详情请查看Events
取消
cancelReminder
仅从状态中移除提醒;计划的action仍可能触发,但会通过状态查找守卫执行空操作。
Actors
  • Key:
    reminderActor["main"]
  • 职责:将提醒存储在持久化状态中,通过
    c.schedule
    为每个提醒触发未来的自action,当计划的action触发时标记提醒为已完成,并向连接的客户端广播
    reminderTriggered
    事件。
  • Actions
    • scheduleReminder
    • scheduleReminderAt
    • triggerReminder
    • getReminders
    • cancelReminder
    • getStats
  • Queues
  • State
    • JSON格式
    • reminders
    • completedCount
生命周期
mermaid
sequenceDiagram
	participant C as Client
	participant R as reminderActor
	participant E as Engine

	C->>R: scheduleReminder(message, delayMs)
	R->>E: schedule.after(delayMs, triggerReminder, id)
	Note over E: schedule persisted + alarm armed
	R-->>C: reminder
	Note over R: actor sleeps
	E->>R: alarm fires, actor wakes
	Note over R: triggerReminder(id) runs
	R-->>C: reminderTriggered event
	Note over R: recurring jobs re-arm here with schedule.after

Security Checklist

安全检查清单

The example is intentionally open: any client can connect to the shared
["main"]
key and schedule or cancel anything. Treat all of the following as required extensions for production:
  • Validate schedule inputs: Clamp
    delayMs
    and
    timestamp
    from clients. Reject negative delays, timestamps in the past, and absurdly far-future deadlines, and bound message or payload sizes.
  • Never schedule client-chosen actions: Expose specific actions like
    scheduleReminder
    that internally arm a fixed callback. Do not pass a client-supplied action name or unchecked args into
    c.schedule
    .
  • Authenticate and scope keys: Add connection authentication and use per-user actor keys instead of one global key, so users cannot read or cancel each other's schedules.
  • Prune completed entries: The example's
    reminders
    array grows without bound. Remove or archive completed entries so state stays small.
  • Use stable IDs: Generate entry IDs with
    crypto.randomUUID()
    rather than timestamp-plus-random strings.
示例是故意开放的:任何客户端都可以连接到共享的
["main"]
key并计划或取消任何任务。以下所有内容均为生产环境必需的扩展:
  • 验证计划任务输入:限制客户端传入的
    delayMs
    timestamp
    。拒绝负延迟、过去的时间戳以及过远的未来截止时间,并限制消息或负载大小。
  • 绝不计划客户端指定的action:仅暴露如
    scheduleReminder
    这类内部触发固定回调的特定action。不要将客户端提供的action名称或未校验的参数传入
    c.schedule
  • 身份验证与Key范围:添加连接身份验证,并使用按用户划分的Actor key而非全局key,确保用户无法读取或取消彼此的计划任务。
  • 清理已完成条目:示例中的
    reminders
    数组会无限增长。请移除或归档已完成的条目,以保持状态精简。
  • 使用稳定ID:使用
    crypto.randomUUID()
    生成条目ID,而非时间戳加随机字符串。

Reference Map

参考地图

Actors

Actors

  • Access Control
  • Actions
  • Actor Keys
  • Actor Scheduling
  • Actor Statuses
  • AI and User-Generated Rivet Actors
  • Authentication
  • Communicating Between Actors
  • Connections
  • Custom Inspector Tabs
  • Debugging
  • Design Patterns
  • Destroying Actors
  • Errors
  • Fetch and WebSocket Handler
  • Helper Types
  • Icons & Names
  • Input Parameters
  • Lifecycle
  • Limits
  • Low-Level HTTP Request Handler
  • Low-Level KV Storage
  • Low-Level WebSocket Handler
  • Metadata
  • Next.js Quickstart
  • Node.js & Bun Quickstart
  • Queues & Run Loops
  • React Quickstart
  • Realtime
  • Rust Quickstart (Preview)
  • Sandbox Actor
  • Scaling & Concurrency
  • Sharing and Joining State
  • SQLite
  • SQLite + Drizzle
  • State & Storage
  • Testing
  • Troubleshooting
  • Types
  • Vanilla HTTP API
  • Versions & Upgrades
  • Workflows
  • 访问控制
  • Actions
  • Actor Keys
  • Actor计划任务
  • Actor状态
  • AI与用户生成的Rivet Actors
  • 身份验证
  • Actor间通信
  • 连接
  • 自定义检查器标签
  • 调试
  • 设计模式
  • 销毁Actors
  • 错误处理
  • Fetch与WebSocket处理器
  • 辅助类型
  • 图标与名称
  • 输入参数
  • 生命周期
  • 限制
  • 底层HTTP请求处理器
  • 底层KV存储
  • 底层WebSocket处理器
  • 元数据
  • Next.js快速入门
  • Node.js & Bun快速入门
  • 队列与运行循环
  • React快速入门
  • 实时功能
  • Rust快速入门(预览版)
  • 沙箱Actor
  • 扩展与并发
  • 状态共享与合并
  • SQLite
  • SQLite + Drizzle
  • 状态与存储
  • 测试
  • 故障排查
  • 类型
  • 原生HTTP API
  • 版本与升级
  • 工作流

Agent Os

Agent OS

  • Agent-to-Agent Communication
  • agentOS vs Sandbox
  • Authentication
  • Benchmarks
  • Configuration
  • Core Package
  • Cron Jobs
  • Deployment
  • Embedded LLM Gateway
  • Events
  • Filesystem
  • Limitations
  • LLM Credentials
  • Multiplayer
  • Networking & Previews
  • Overview
  • Permissions
  • Persistence & Sleep
  • Pi
  • Processes & Shell
  • Queues
  • Quickstart
  • Sandbox Mounting
  • Security & Auth
  • Security Model
  • Sessions
  • Software
  • SQLite
  • System Prompt
  • Tools
  • Webhooks
  • Workflow Automation
  • Agent间通信
  • agentOS vs 沙箱
  • 身份验证
  • 基准测试
  • 配置
  • 核心包
  • 定时任务
  • 部署
  • 嵌入式LLM网关
  • 事件
  • 文件系统
  • 限制
  • LLM凭证
  • 多人功能
  • 网络与预览
  • 概述
  • 权限
  • 持久化与休眠
  • Pi
  • 进程与Shell
  • 队列
  • 快速入门
  • 沙箱挂载
  • 安全与身份验证
  • 安全模型
  • 会话
  • 软件
  • SQLite
  • 系统提示词
  • 工具
  • Webhooks
  • 工作流自动化

Clients

客户端

  • Node.js & Bun
  • React
  • Swift
  • SwiftUI
  • Node.js & Bun
  • React
  • Swift
  • SwiftUI

Connect

部署连接

  • Deploy To Amazon Web Services Lambda
  • Deploying to AWS ECS
  • Deploying to Cloudflare Workers
  • Deploying to Freestyle
  • Deploying to Google Cloud Run
  • Deploying to Hetzner
  • Deploying to Kubernetes
  • Deploying to Railway
  • Deploying to Rivet Compute
  • Deploying to Supabase Functions
  • Deploying to Vercel
  • Deploying to VMs & Bare Metal
  • 部署到Amazon Web Services Lambda
  • 部署到AWS ECS
  • 部署到Cloudflare Workers
  • 部署到Freestyle
  • 部署到Google Cloud Run
  • 部署到Hetzner
  • 部署到Kubernetes
  • 部署到Railway
  • 部署到Rivet Compute
  • 部署到Supabase Functions
  • 部署到Vercel
  • 部署到虚拟机与裸金属服务器

Cookbook

实战指南

  • AI Agent
  • AI Agent Workspaces
  • Chat Room
  • Collaborative Text Editor
  • Cron Jobs and Scheduled Tasks
  • Database per Tenant
  • Deploying Rivet in a VPC or Air-Gapped Network
  • Live Cursors and Presence
  • Multiplayer Game
  • AI Agent
  • AI Agent工作区
  • 聊天室
  • 协作文本编辑器
  • 定时任务与计划任务
  • 租户专属数据库
  • 在VPC或隔离网络中部署Rivet
  • 实时光标与在线状态
  • 多人游戏

General

通用指南

  • Actor Configuration
  • Architecture
  • Cross-Origin Resource Sharing
  • Documentation for LLMs & AI
  • Edge Networking
  • Endpoints
  • Environment Variables
  • HTTP Server
  • Logging
  • Pool Configuration
  • Production Checklist
  • Registry Configuration
  • Runtime Modes
  • Actor配置
  • 架构
  • 跨域资源共享(CORS)
  • 面向LLM与AI的文档
  • 边缘网络
  • 端点
  • 环境变量
  • HTTP服务器
  • 日志
  • 池配置
  • 生产环境检查清单
  • 注册表配置
  • 运行时模式

Self Hosting

自托管

  • Configuration
  • Docker Compose
  • Docker Container
  • File System
  • FoundationDB (Enterprise)
  • Installing Rivet Engine
  • Kubernetes
  • Multi-Region
  • PostgreSQL
  • Production Checklist
  • Railway Deployment
  • Render Deployment
  • TLS & Certificates
  • 配置
  • Docker Compose
  • Docker容器
  • 文件系统
  • FoundationDB(企业版)
  • 安装Rivet引擎
  • Kubernetes
  • 多区域部署
  • PostgreSQL
  • 生产环境检查清单
  • Railway部署
  • Render部署
  • TLS与证书