What
Replace the per-pod setInterval scheduling in backend/src/jobs/part49Jobs.ts with a leader-elected or externally-triggered scheduling mechanism so daily jobs run exactly once, at a predictable time.
Why
The docstring in part49Jobs.ts claims the jobs are "Designed to run once per day... at midnight UTC," but the actual implementation (L13-28) is a plain setInterval(fn, 24h) started from each pod's boot time, called unconditionally in every replica via index.ts. With k8s/base/backend-hpa.yaml setting minReplicas: 2, both the daily usage snapshot and nightly integrity check run redundantly on every pod, at whatever time each pod happened to boot — not midnight, and drifting further with every deploy/restart/scale event.
Scope
In scope:
- Either: (a) add a leader-election mechanism (e.g. a Postgres advisory lock claimed once per day) so only one pod executes the job, or (b) move these jobs to a proper scheduled trigger (k8s CronJob) external to the app pods
- Ensure the job actually runs at a fixed, predictable time (e.g. midnight UTC) rather than pod-boot-relative
- Update the docstring to match actual behavior, or make behavior match the docstring — whichever approach is chosen
Out of scope:
- The payroll scheduler cron (
scheduleExecutor.ts), covered by a separate issue
- The content/logic of the daily usage snapshot or integrity check themselves
Acceptance Criteria
Technical Context
backend/src/jobs/part49Jobs.ts — docstring L10-11, setInterval implementation L13-28
backend/src/index.ts — unconditional job init in every replica
k8s/base/backend-hpa.yaml — minReplicas: 2
What
Replace the per-pod
setIntervalscheduling inbackend/src/jobs/part49Jobs.tswith a leader-elected or externally-triggered scheduling mechanism so daily jobs run exactly once, at a predictable time.Why
The docstring in
part49Jobs.tsclaims the jobs are "Designed to run once per day... at midnight UTC," but the actual implementation (L13-28) is a plainsetInterval(fn, 24h)started from each pod's boot time, called unconditionally in every replica viaindex.ts. Withk8s/base/backend-hpa.yamlsettingminReplicas: 2, both the daily usage snapshot and nightly integrity check run redundantly on every pod, at whatever time each pod happened to boot — not midnight, and drifting further with every deploy/restart/scale event.Scope
In scope:
Out of scope:
scheduleExecutor.ts), covered by a separate issueAcceptance Criteria
Technical Context
backend/src/jobs/part49Jobs.ts— docstring L10-11,setIntervalimplementation L13-28backend/src/index.ts— unconditional job init in every replicak8s/base/backend-hpa.yaml—minReplicas: 2