Royal Glow internal docs · now fully interactive — Steps, API tables, file trees & live status
Royal Glow Docs

Background Jobs

The 19 scheduled and event-driven jobs that keep Royal Glow running — 14 QStash scheduled, 4 QStash triggered, 1 GitHub Actions cron.

Background Jobs

19 jobs total — 14 QStash scheduled + 4 QStash triggered + 1 GitHub Actions cron. All scheduled and event-driven jobs run on QStash (HTTP → Next.js API routes hosted in apps/admin, served from admin.theroyalglow.in/api/jobs/*). The single exception is the pprd branch reset (Job 5), a Neon control-plane op on GitHub Actions cron. Every job pings a BetterStack heartbeat on success.

Royal Glow runs 19 background jobs. All scheduled and event-driven jobs run on QStash (HTTP → Next.js API routes) — including the pure-SQL maintenance jobs, which execute their idempotent SQL via @rgss/db query functions. The single exception is the pprd branch reset (Job 5), which is a Neon control-plane op and runs on GitHub Actions cron.

pg_cron is not used. The 6 DB-maintenance jobs were originally planned on pg_cron, but the free-tier prod Neon compute scales to zero after ~5 min idle and pg_cron only fires while the compute is awake — the late-night windows (11:30 PM–2:30 AM IST) would silently never run. QStash POSTs an endpoint that wakes the compute, so the jobs run reliably at ₹0. The job SQL was ported verbatim into packages/db/src/queries/jobs.ts.

Where jobs run

Upstash HTTP queue → Next.js API routes in apps/admin. Use for ALL scheduled and triggered jobs (DB maintenance + external services).

Every QStash job calls a POST /api/jobs/... route and is auto-retried (3x, exponential backoff) on a non-2xx response. Every job pings a BetterStack heartbeat on success so silent failures get detected. The /api/jobs/* routes are served from admin.theroyalglow.in/api/jobs/*; QStash schedules are registered from the admin deploy.

GitHub CI. Control-plane only — Neon pprd branch reset + PII strip (Job 5). A Neon Branch Reset API call cannot run inside a normal request, so it lives in CI.

All schedules are in UTC; the salon operates IST (UTC+5:30).

Not jobs (synchronous / external)

Three time-critical actions deliberately run in-request, not as jobs:

  • Invoice email + PDF — fired immediately when the receptionist marks payment received.
  • Gems-earned push — fired in the same request as invoice completion.
  • Re-engagement email — handled inside Brevo automations (marketing), not counted here.

Job inventory

These run in the early IST hours when traffic is zero. Jobs 1–4, 6 and 7 run on QStash (each POSTs its /api/jobs/... route, which runs the idempotent SQL via a @rgss/db query function) — not pg_cron. Job 5 runs via GitHub Actions cron (Neon branch reset) plus SQL anonymisation.

HTTP-triggered application jobs on a fixed cadence (the 6 DB-maintenance jobs are also QStash-scheduled, for 14 QStash-scheduled jobs in total).

Reminders fire only when the customer has the relevant preference enabled (appointment_reminders_enabled, membership_alerts_enabled) or marketing_consent for birthday/marketing sends. Each job writes a notification row for idempotency so the same alert is never sent twice.

Event-driven — enqueued with a delay by an API route when a business event occurs, not on a fixed schedule.

OpenReport an issue

Was this page helpful?

On this page