All Cron Scheduler Jobs
Overview
The cron-scheduler profile is a single process that bundles 10 timed jobs and
4 consumers (mbox handoff/callback, booking notification/event).
Design principle: most crons don't do the heavy work themselves — they only figure out "what's due now" and publish jobs onto a queue for the real consumer to handle. That lets heavy work scale independently of the timer, and overlapping cron fires across multiple replicas don't produce duplicate effects, because claiming work is atomic at the DB/Redis level.
It uses robfig/cron, running on the server's local time just like the original NestJS
@Cron decorators (per-rule Asia/Bangkok timezone handling lives inside
ScheduledTriggerScannerService.processRule).
Business Flow
All 10 cron jobs
| Cron | Table/Data Source | What it does | Docs |
|---|---|---|---|
*/1 * * * * scheduledTrigger.Run | trigger_rule (source_type='scheduled') | Finds due rules (per-rule timezone math) and publishes scheduled_trigger_fire | trigger-engine |
*/1 * * * * scheduledAction.Run | scheduled_action | Recovers rows stuck for 5 minutes, claims 500 rows with FOR UPDATE SKIP LOCKED, publishes scheduled_action_fire | scheduled-action |
0 * * * * campaignStat.Run | Redis CAMPAIGN_STAT_DIRTY:* | Finds campaigns with new data and publishes calculate_campaign_stat | campaign-stat-calculation |
0 * * * * richMenuStat.Run | Redis RICH_MENU_STAT_DIRTY:* | Finds rich menus with new data and publishes calculate_rich_menu_stat_item | rich-menu-stat-calculation |
*/5 * * * * autoResponseSync.Run | line_oa + auto_response | Rebuilds the Redis keyword hash for every active OA (a broken OA uses continue, not return) | auto-response |
*/1 * * * * mboxTimeout.Run | Redis sorted set agent_sessions | Warns (stage 1) and closes (stage 2) agent sessions that have timed out | mbox-agent-handoff |
*/1 * * * * bookingReminder.Run | appointment.booking + journey | Finds bookings due for a reminder and publishes booking_notification | booking-notification |
*/1 * * * * bookingNoShow.Run | appointment.booking + journey | Finds bookings past time plus a grace period and marks them no-show | booking-notification |
*/1 * * * * campaignSchedule.Run | Redis tick key | Publishes process_campaign once per minute (deduped across replicas with SET NX) | campaign-schedule-dispatch |
0 3 * * * loyaltyTier.Run | loyalty.program / tier / account | Recalculates membership tiers platform-wide (the only path that can demote) | loyalty-tier-recalculation |
The 4 consumers that run alongside them
mbox_handoff— see mbox-agent-handoffmbox_callback— see mbox-callbackbooking_notification— see booking-notificationbooking_event_trigger— see booking-event-trigger
Startup sequence
runCronSchedulercreates themq.Clientand asserts the topology- Assembles every service (mbox handoff/callback, booking notification/event, all 10 scanners), sharing the same pgx pool / RedisService / mq publisher / Mbox REST client
cron.New()thenAddFuncfor each job by its expression; a registration error stops the profile immediatelyc.Start(), withcontext.AfterFunc(ctx, c.Stop)wired up for a clean stop on shutdown- Registers all 4 consumers on the
mq.Registrarand runs them as goroutines
Key Files & Functions
cmd/worker/main.go—runCronScheduler(ctx, a)(defines thejobs []cronJoblist of 10)internal/cronscheduler/— one file per scanner:scheduled_trigger_scanner.go,scheduled_action_scanner.go,campaign_stat_scanner.go,rich_menu_stat_scanner.go,auto_response_sync.go,mbox_timeout.go,booking_reminder.go,booking_noshow.go,campaign_schedule_scanner.go,loyalty_tier.gointernal/cronscheduler/helpers.go—collectRows()(converts pgx rows into[]map[string]any)internal/cronscheduler/health.go— the profile's health surface- Tests:
campaign_schedule_scanner_test.go,loyalty_tier_test.go
Connections to Other Services
- PostgreSQL — every scanner except the campaign/rich-menu stat ones (which read Redis)
- Redis — dirty keys for the stat scanners, tick dedup for campaign schedule,
agent_sessionsfor mbox timeout, and the auto-response keyword hash - RabbitMQ — publishes work to destination queues and consumes 4 queues
- Mbox REST API and LINE API — via the mbox / booking services
- ENV:
ENABLE_CRON_SCANNING(app-level toggle),CRON_SCHEDULER_PORT(default 3003)