Review

Nightly promoter settlement invoices via in-process APScheduler

Fathomfee settles small-venue shows after doors close. Ops wants promoter invoices (PDF + Stripe draft) out by 02:15 UTC without keeping Celery Beat solely for one job. Register a BackgroundScheduler CronTrigger inside the API process so every deploy that can serve traffic also owns the nightly settlement send.

APSchedulerTier 5crondistributed-lockreplicasinvoicing

Click a line to flag it, pick one or more labels, then submit. If the change looks correct, approve it.

fathomfee/jobs/promoter_settlements.py+19-6
11"""Nightly promoter settlement invoices for last-night's shows."""
22
33from apscheduler.schedulers.background import BackgroundScheduler
44from apscheduler.triggers.cron import CronTrigger
55
66from fathomfee.billing import create_and_email_settlement_invoice
77from fathomfee.shows import closed_shows_for_night
88
9+# Run inside each API task so we do not need a dedicated Beat worker for one cron.
10+scheduler = BackgroundScheduler(timezone="UTC")
11+
912def send_promoter_settlements() -> None:
1013 for show in closed_shows_for_night():
1114 create_and_email_settlement_invoice(show.id, show.promoter_id)
1215
13-# Invoked only by the single Celery Beat process (see deploy/celery-beat).
14-def register_beat_task(app) -> None:
15- app.conf.beat_schedule["promoter-settlements"] = {
16- "task": "fathomfee.jobs.promoter_settlements.send_promoter_settlements",
17- "schedule": crontab(hour=2, minute=15),
18- }
16+def register_jobs() -> None:
17+ # max_instances prevents overlap if a send runs long on *this* process.
18+ scheduler.add_job(
19+ send_promoter_settlements,
20+ CronTrigger(hour=2, minute=15),
21+ id="promoter-settlements-nightly",
22+ replace_existing=True,
23+ max_instances=1,
24+ coalesce=True,
25+ )
26+ scheduler.start()
1927
28+
29+# Called from FastAPI lifespan on every replica that boots.
30+def on_app_startup() -> None:
31+ register_jobs()
32+
2033# create_and_email_settlement_invoice always creates a new Stripe invoice + PDF.