Three periodic background jobs ran on every instance with no cross-instance
coordination, multiplying their cost (and side effects) by the replica count:
- DashboardAggregationService.runScheduledAggregation: N× heavy GROUP BY
aggregation queries every minute plus watermark write races.
- PaymentOrderExpiryService.runOnce: N× upstream payment-provider reconcile/
expiry API calls per pending order.
- SubscriptionExpiryService.sendExpiryReminders: N× full active-subscription
scans every minute and potential duplicate reminder emails.
Add a LeaderLockCache abstraction so only one instance runs each job per cycle:
- The interface lives in the service layer; the Redis-backed implementation
(SetNX + compare-and-delete release) lives in the repository layer, so the
service package keeps its depguard "must not import redis" boundary intact.
- tryAcquireSingletonLeaderLock prefers the cache and falls back to a Postgres
advisory lock when Redis errors, mirroring the Ops background services. When
neither backend is configured the job runs ungated, preserving single-instance
and test behavior (no self-lockout: the lock is released every cycle).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>