$npx -y skills add waynesutton/convexskills --skill convex-cron-jobsScheduled function patterns for background tasks including interval scheduling, cron expressions, job monitoring, retry strategies, and best practices for long-running tasks
| 1 | # Convex Cron Jobs |
| 2 | |
| 3 | Schedule recurring functions for background tasks, cleanup jobs, data syncing, and automated workflows in Convex applications. |
| 4 | |
| 5 | ## Documentation Sources |
| 6 | |
| 7 | Before implementing, do not assume; fetch the latest documentation: |
| 8 | |
| 9 | - Primary: https://docs.convex.dev/scheduling/cron-jobs |
| 10 | - Scheduling Overview: https://docs.convex.dev/scheduling |
| 11 | - Scheduled Functions: https://docs.convex.dev/scheduling/scheduled-functions |
| 12 | - For broader context: https://docs.convex.dev/llms.txt |
| 13 | |
| 14 | ## Instructions |
| 15 | |
| 16 | ### Cron Jobs Overview |
| 17 | |
| 18 | Convex cron jobs allow you to schedule functions to run at regular intervals or specific times. Key features: |
| 19 | |
| 20 | - Run functions on a fixed schedule |
| 21 | - Support for interval-based and cron expression scheduling |
| 22 | - Automatic retries on failure |
| 23 | - Monitoring via the Convex dashboard |
| 24 | |
| 25 | ### Basic Cron Setup |
| 26 | |
| 27 | ```typescript |
| 28 | // convex/crons.ts |
| 29 | import { cronJobs } from "convex/server"; |
| 30 | import { internal } from "./_generated/api"; |
| 31 | |
| 32 | const crons = cronJobs(); |
| 33 | |
| 34 | // Run every hour |
| 35 | crons.interval( |
| 36 | "cleanup expired sessions", |
| 37 | { hours: 1 }, |
| 38 | internal.tasks.cleanupExpiredSessions, |
| 39 | {} |
| 40 | ); |
| 41 | |
| 42 | // Run every day at midnight UTC |
| 43 | crons.cron( |
| 44 | "daily report", |
| 45 | "0 0 * * *", |
| 46 | internal.reports.generateDailyReport, |
| 47 | {} |
| 48 | ); |
| 49 | |
| 50 | export default crons; |
| 51 | ``` |
| 52 | |
| 53 | ### Interval-Based Scheduling |
| 54 | |
| 55 | Use `crons.interval` for simple recurring tasks: |
| 56 | |
| 57 | ```typescript |
| 58 | // convex/crons.ts |
| 59 | import { cronJobs } from "convex/server"; |
| 60 | import { internal } from "./_generated/api"; |
| 61 | |
| 62 | const crons = cronJobs(); |
| 63 | |
| 64 | // Every 5 minutes |
| 65 | crons.interval( |
| 66 | "sync external data", |
| 67 | { minutes: 5 }, |
| 68 | internal.sync.fetchExternalData, |
| 69 | {} |
| 70 | ); |
| 71 | |
| 72 | // Every 2 hours |
| 73 | crons.interval( |
| 74 | "cleanup temp files", |
| 75 | { hours: 2 }, |
| 76 | internal.files.cleanupTempFiles, |
| 77 | {} |
| 78 | ); |
| 79 | |
| 80 | // Every 30 seconds (minimum interval) |
| 81 | crons.interval( |
| 82 | "health check", |
| 83 | { seconds: 30 }, |
| 84 | internal.monitoring.healthCheck, |
| 85 | {} |
| 86 | ); |
| 87 | |
| 88 | export default crons; |
| 89 | ``` |
| 90 | |
| 91 | ### Cron Expression Scheduling |
| 92 | |
| 93 | Use `crons.cron` for precise scheduling with cron expressions: |
| 94 | |
| 95 | ```typescript |
| 96 | // convex/crons.ts |
| 97 | import { cronJobs } from "convex/server"; |
| 98 | import { internal } from "./_generated/api"; |
| 99 | |
| 100 | const crons = cronJobs(); |
| 101 | |
| 102 | // Every day at 9 AM UTC |
| 103 | crons.cron( |
| 104 | "morning notifications", |
| 105 | "0 9 * * *", |
| 106 | internal.notifications.sendMorningDigest, |
| 107 | {} |
| 108 | ); |
| 109 | |
| 110 | // Every Monday at 8 AM UTC |
| 111 | crons.cron( |
| 112 | "weekly summary", |
| 113 | "0 8 * * 1", |
| 114 | internal.reports.generateWeeklySummary, |
| 115 | {} |
| 116 | ); |
| 117 | |
| 118 | // First day of every month at midnight |
| 119 | crons.cron( |
| 120 | "monthly billing", |
| 121 | "0 0 1 * *", |
| 122 | internal.billing.processMonthlyBilling, |
| 123 | {} |
| 124 | ); |
| 125 | |
| 126 | // Every 15 minutes |
| 127 | crons.cron( |
| 128 | "frequent sync", |
| 129 | "*/15 * * * *", |
| 130 | internal.sync.syncData, |
| 131 | {} |
| 132 | ); |
| 133 | |
| 134 | export default crons; |
| 135 | ``` |
| 136 | |
| 137 | ### Cron Expression Reference |
| 138 | |
| 139 | ``` |
| 140 | ┌───────────── minute (0-59) |
| 141 | │ ┌───────────── hour (0-23) |
| 142 | │ │ ┌───────────── day of month (1-31) |
| 143 | │ │ │ ┌───────────── month (1-12) |
| 144 | │ │ │ │ ┌───────────── day of week (0-6, Sunday=0) |
| 145 | │ │ │ │ │ |
| 146 | * * * * * |
| 147 | ``` |
| 148 | |
| 149 | Common patterns: |
| 150 | - `* * * * *` - Every minute |
| 151 | - `0 * * * *` - Every hour |
| 152 | - `0 0 * * *` - Every day at midnight |
| 153 | - `0 0 * * 0` - Every Sunday at midnight |
| 154 | - `0 0 1 * *` - First day of every month |
| 155 | - `*/5 * * * *` - Every 5 minutes |
| 156 | - `0 9-17 * * 1-5` - Every hour from 9 AM to 5 PM, Monday through Friday |
| 157 | |
| 158 | ### Internal Functions for Crons |
| 159 | |
| 160 | Cron jobs should call internal functions for security: |
| 161 | |
| 162 | ```typescript |
| 163 | // convex/tasks.ts |
| 164 | import { internalMutation, internalQuery } from "./_generated/server"; |
| 165 | import { v } from "convex/values"; |
| 166 | |
| 167 | // Cleanup expired sessions |
| 168 | export const cleanupExpiredSessions = internalMutation({ |
| 169 | args: {}, |
| 170 | returns: v.number(), |
| 171 | handler: async (ctx) => { |
| 172 | const oneHourAgo = Date.now() - 60 * 60 * 1000; |
| 173 | |
| 174 | const expiredSessions = await ctx.db |
| 175 | .query("sessions") |
| 176 | .withIndex("by_lastActive") |
| 177 | .filter((q) => q.lt(q.field("lastActive"), oneHourAgo)) |
| 178 | .collect(); |
| 179 | |
| 180 | for (const session of expiredSessions) { |
| 181 | await ctx.db.delete(session._id); |
| 182 | } |
| 183 | |
| 184 | return expiredSessions.length; |
| 185 | }, |
| 186 | }); |
| 187 | |
| 188 | // Process pending tasks |
| 189 | export const processPendingTasks = internalMutation({ |
| 190 | args: {}, |
| 191 | returns: v.null(), |
| 192 | handler: async (ctx) => { |
| 193 | const pendingTasks = await ctx.db |
| 194 | .query("tasks") |
| 195 | .withIndex("by_status", (q) => q.eq("status", "pending")) |
| 196 | .take(100); |
| 197 | |
| 198 | for (const task of pendingTasks) { |
| 199 | await ctx.db.patch(task._id, { |
| 200 | status: "processing", |
| 201 | startedAt: Date.now(), |
| 202 | }); |
| 203 | |
| 204 | // Schedule the actual processing |
| 205 | await ctx.scheduler.runAfter(0, internal.tasks.processTask, { |
| 206 | taskId: task._id, |
| 207 | }); |
| 208 | } |
| 209 | |
| 210 | return null; |
| 211 | }, |
| 212 | }); |
| 213 | ``` |
| 214 | |
| 215 | ### Cron Jobs with Arguments |
| 216 | |
| 217 | Pass static arguments to cron jobs: |
| 218 | |
| 219 | ```typescri |