Scheduled Jobs & Workers
Background work runs on BullMQ (Upstash Redis). Three queues are consumed by workers running in the API process. The admin Bull Board at /admin/queues visualises them.
Queues & workers
| Queue | Worker file | Responsibility |
|---|---|---|
sms-notifications | workers/smsWorker.ts | Send SMS via MSG91 (3 retries, backoff 1s/5s/30s). Logs + skips on failure; never crashes. |
scheduled | workers/scheduledWorker.ts | Dispatches the recurring cron jobs below. |
search-sync | workers/searchSyncWorker.ts | Sync batch documents to Meilisearch. |
Queue definitions + enqueuers live in queues/ (smsQueue.ts, scheduledQueue.ts, searchSyncQueue.ts). Each queue handle is null when Redis isn't configured, and the system degrades to in-process behaviour.
Recurring jobs (scheduledWorker.ts)
| Job | Schedule | Action |
|---|---|---|
expire-counters | hourly | COUNTERED offers/bids past counterValidUntil → REJECTED |
generate-nudges | 08:00 IST daily | Profile-completion nudges (snooze-aware) |
auto-reject-stale-suggestions | 09:00 IST daily | Taxonomy suggestions pending > 15 days → REJECTED |
expire-leads | 06:00 IST daily | ACTIVE leads past expiresAt → EXPIRED; auto-reject outstanding engagements |
prune-search-events | 03:00 IST Sunday | Delete SearchEvents older than 90 days |
overcommit-grace-check | hourly | Stock-overcommit heuristic → STOCK_ALERT notification |
trust-score-daily-sweep | 07:00 IST daily | Recompute trust scores for all Active Users |
keyword-trigger-sweep | 04:30 UTC daily | Match new products (24h) against saved searches (30d) → digest |
Additional fulfilment/grievance sweeps (introduced later): dispatch-nudge-sweep, receipt-expiry-sweep (14-day, prompts the seller — never auto-cancels), grievance-sla-sweep (auto-escalates SLA breaches), and the finalize-pending-deletions sweep (FINALIZE_PENDING_DELETIONS_CRON, default 10:00 IST).
Enqueue → consume model
Operational notes
- No inline external calls. A request handler must enqueue, never call MSG91/Meili directly. The handler returns immediately; the worker does the slow/failable work.
- Failure isolation. A failed SMS job logs full context and is retried; it never crashes the worker or the API.
- Idempotent notifications.
Notification.dedupeKey(userId:type:istDate) prevents duplicate nudges. - Timezone. Quota boundaries and most sweeps use IST (
Asia/Kolkata) vialib/timezone.ts; a couple of jobs use UTC where noted. - Visibility. Watch job health in the Bull Board (
/admin/queues, admin auth) — it registers both the SMS and scheduled queues.
