JOB-EASY

Scheduled Jobs

How background tasks are orchestrated around execution limits.

Job-Easy has 14 robust background cron jobs for syncing emails, polling ATS boards, draining mail queues, and cleaning up stale accounts.

The Vercel Limitation Bypass

The vercel.json file defines the crons. However, Vercel's Hobby plan rejects any cron expression that would fire more than once a day. The deployment literally fails if you try to deploy a sub-daily cron on a Hobby account. Because of this, the vercel.json expressions are essentially "once-a-day backstops".

To restore real cadence (e.g. syncing Gmail replies every 10 minutes, or draining the mail queue every minute), the endpoints accept a secure secret header:

http
Authorization: Bearer $CRON_SECRET

This allows an external scheduler to ping the endpoints securely at their intended frequency, bypassing Vercel's platform limits completely while keeping the jobs serverless.

The Two-Write Lifecycle

Every job is wrapped by a strict cronHandler() function that orchestrates the JobRun audit log. Because serverless functions are unceremoniously killed when they hit their execution ceiling, a killed function cannot record its own failure.

To solve this, the wrapper explicitly issues two writes per run:

  1. It writes a running status before the handler executes.
  2. It writes success or failed after completion.

A dedicated reaper job (/api/cron/purge-stale) sweeps the database for old running rows and safely marks them as timeout, cleanly distinguishing a platform execution kill from a code-level exception.

Manual Invocation

When an admin runs a job manually from the Monitoring dashboard (POST /api/admin/monitor/jobs/[job]/run), the system makes an internal HTTP call to the job's public route rather than importing the handler directly.

This ensures the manual run traverses the exact same middleware and cronHandler() instrumentation as the automated scheduler. There is no SSRF surface because the requested job key is strictly validated against the internal CRON_JOBS registry before the HTTP call is constructed.

Queue Concurrency

Jobs that pull from a queue (like the mail drain) use a FOR UPDATE SKIP LOCKED raw SQL query to claim rows. This structurally prevents two concurrent cron invocations from accidentally grabbing and double-sending the same message if their execution windows overlap.