diff --git a/.env.example b/.env.example index 0de88aa..85b26c0 100644 --- a/.env.example +++ b/.env.example @@ -30,6 +30,6 @@ PR_RECONCILE_INTERVAL_MS=3600000 PR_RECONCILE_WINDOW_DAYS=45 # Nightly full repo backfill (coarse safety net; heavier — set false to disable). +# Runs at 12:10am America/Chicago (hardcoded cron); window in days below. NIGHTLY_BACKFILL_ENABLED=true -NIGHTLY_BACKFILL_INTERVAL_MS=86400000 -NIGHTLY_BACKFILL_DAYS=40 +NIGHTLY_BACKFILL_DAYS=10 diff --git a/packages/das/src/webhook/repo-backfill-schedule.service.ts b/packages/das/src/webhook/repo-backfill-schedule.service.ts index 2494605..f7c4c49 100644 --- a/packages/das/src/webhook/repo-backfill-schedule.service.ts +++ b/packages/das/src/webhook/repo-backfill-schedule.service.ts @@ -1,9 +1,5 @@ -import { - Injectable, - Logger, - OnModuleDestroy, - OnModuleInit, -} from "@nestjs/common"; +import { Injectable, Logger, OnModuleInit } from "@nestjs/common"; +import { Cron } from "@nestjs/schedule"; import { InjectQueue } from "@nestjs/bullmq"; import { InjectRepository } from "@nestjs/typeorm"; import { Queue } from "bullmq"; @@ -22,19 +18,17 @@ import { // Heavier than the reconcile sweep (re-touches every PR in the window), so it // runs daily and can be disabled on critical infra via env. const BACKFILL_ENABLED = process.env.NIGHTLY_BACKFILL_ENABLED !== "false"; -const BACKFILL_INTERVAL_MS = Number( - process.env.NIGHTLY_BACKFILL_INTERVAL_MS ?? 24 * 60 * 60 * 1000, // daily -); +// 12:10am America/Chicago. Anchored to the wall clock (not boot) so redeploys +// don't move the window; timeZone keeps it at local midnight across CST/CDT. +const BACKFILL_CRON = "10 0 * * *"; +const BACKFILL_TZ = "America/Chicago"; const BACKFILL_DAYS = Number( process.env.NIGHTLY_BACKFILL_DAYS ?? DEFAULT_BACKFILL_DAYS, ); @Injectable() -export class RepoBackfillScheduleService - implements OnModuleInit, OnModuleDestroy -{ +export class RepoBackfillScheduleService implements OnModuleInit { private readonly logger = new Logger(RepoBackfillScheduleService.name); - private timer: NodeJS.Timeout | null = null; constructor( @InjectRepository(Repo) @@ -50,19 +44,16 @@ export class RepoBackfillScheduleService ); return; } - // Unlike the reconcile sweep, don't run at startup — a deploy already - // implies fresh data, and this is the heavy job. Start on the interval. - this.timer = setInterval( - () => void this.backfillAll(), - BACKFILL_INTERVAL_MS, + this.logger.log( + `Nightly repo backfill scheduled '${BACKFILL_CRON}' (${BACKFILL_TZ})`, ); } - onModuleDestroy(): void { - if (this.timer) clearInterval(this.timer); - } - + // No run-at-startup (a deploy already implies fresh data, and this is heavy); + // the static per-repo jobId dedupes a tick that overlaps a draining run. + @Cron(BACKFILL_CRON, { name: "nightly-backfill", timeZone: BACKFILL_TZ }) private async backfillAll(): Promise { + if (!BACKFILL_ENABLED) return; try { const repos = await this.repoRepo.find({ where: { registered: true },