Scheduled tasks
Understand the project's three built-in Cloudflare Cron tasks and the configuration and entry points to maintain when adding a task.
Saavo uses Cloudflare Workers Cron Triggers to start system maintenance periodically. The project currently registers three schedules for entitlement cycle checks, expired data cleanup, and analytics retention cleanup.
Cron only triggers work on schedule. The work may finish within the scheduled invocation or become a Reaction event processed by a background queue. When troubleshooting, follow the subsequent execution flow rather than checking only whether Cloudflare triggered Cron.
Built-in scheduled tasks
Cloudflare Cron runs in UTC. The current configuration is:
| Cron expression | Entry point constant | Schedule | Work performed |
|---|---|---|---|
0 0 * * * | ENTITLEMENT_CYCLE_CRON | Daily at 00:00 UTC | Emits an entitlement cycle due event, with due records handled by background tasks |
0 1 * * * | DATA_CLEANUP_CRON | Daily at 01:00 UTC | Emits a daily data cleanup event |
30 */6 * * * | ANALYTICS_RETENTION_CRON | At minute 30 every 6 hours | Directly deletes raw analytics events beyond the retention period |
Cron expressions appear in two places:
triggers.cronsinwrangler.jsoncdetermines when Cloudflare invokes the Worker.- Constants and
scheduledEntrybranches insrc/entry/index.tsdetermine which work runs after a trigger.
The strings must match exactly. The current entry point has no dedicated error or alert for unknown expressions. If you change only one location, Cloudflare may still show a successful trigger without any business branch running.
How each task runs
Process entitlement cycles
The daily Cron finds the next due record and emits EntitlementCycleDueEvent. The subsequent process-due-entitlement-cycles is an asynchronous Command that processes at most 30 due records per batch by default.
The entitlement system also uses a Timer Durable Object to schedule the next precise reset, allowing a 10-second buffer. The daily Cron therefore serves mainly as a periodic check and recovery mechanism. It is not the only timer used for entitlement resets.
Clean up expired business data
The data cleanup task generates a stable idempotency key from the UTC date. Repeated triggers on the same day produce only one effective event. The background Command removes expired sign-in sessions, email verification sessions, password reset sessions, OAuth authorization sessions, invalid OAuth tokens and authorization codes, and eligible soft-deleted OAuth clients.
Most data uses a cutoff of one day ago. Two-factor authentication setup sessions are eligible for cleanup as soon as they expire. Do not increase Cron frequency to change the retention policy. Adjust the rules in the service that owns the data.
Clean up expired analytics data
Analytics cleanup operates on ANALYTICS_DB directly in the scheduled runtime and does not enter async-policy-task. It deletes expired raw events in batches within its execution budget. Any unfinished cleanup is left for subsequent Cron runs.
analytics.retention.rawDays in config/deploy.ts controls the retention period. Changing rawDays does not require changing the Cron expression. See Analytics for details.
Add a scheduled task
When adding a Cron task, you should make the following changes:
- Add the expression to
triggers.cronsinwrangler.jsonc. - Define the corresponding constant in
src/entry/index.ts. - Add an exactly matching branch in
scheduledEntry. - Reuse the scheduled
WorkerCtxalready created by the entry point and delegate business rules to the service that owns them. - For work that takes longer, requires reliable retries, or calls external services, use a Reaction Event / Command to enter the existing background task pipeline.
- Add tests for triggering, repeated execution, and failures in the new branch.
Do not write SQL directly in scheduledEntry or call resolveFetchWorkerCtx from a non-HTTP entry point. Business work that needs asynchronous execution can be queued through Reaction. See Background jobs for how it runs afterward.
A scheduled trigger does not mean the work is complete
Entitlement cycle processing and data cleanup pass through Reaction and queues after triggering. A successful Cloudflare Cron record proves only that the scheduled entry point was invoked. Determine the final outcome from Reaction execution records, logs, and data state.
Deployment and troubleshooting
After deployment, check the Cloudflare dashboard to confirm that Cron triggers on schedule. For tasks that enter Reaction, also check:
/dashboard/reaction/events
/dashboard/reaction/commandsAnalytics cleanup does not use Reaction. Check scheduled logs, alerts, and whether data beyond the retention period in ANALYTICS_DB continues to decrease.
If a task does not run, troubleshoot in this order:
- Compare
wrangler.jsoncwith the entry point constants, including exact strings and spaces. - Confirm that the deployment environment uses the latest Worker configuration.
- Confirm that Cloudflare has produced a Cron trigger record.
- Check entry point logs to see whether the expected branch ran.
- For tasks that enter Reaction, check events, Commands, and queue consumers.
- For directly executed analytics cleanup, check database bindings, retention settings, and backlog alerts.
Pre-launch checks
- The three expressions in
wrangler.jsoncexactly match the entry point constants. - Expected execution times have been checked in UTC.
- All three built-in tasks have run successfully at least once in the deployment environment.
- Entitlement cycle and data cleanup Reaction events and Commands have normal execution records.
- Analytics cleanup has no persistent backlog alerts indicating that incoming data outpaces cleanup.
- New tasks cover repeated triggers, partial failures, and safe retries.
- The scheduled entry point only dispatches work. It does not bypass services or repositories to access the business database directly.
Frequently asked questions
Next steps
- Understand execution after queuing: Background jobs
- Adjust analytics retention: Analytics
- Learn about entitlement cycles: Permissions and entitlements