fix(api): gate catalog backfill on prod host, not NODE_ENV
dev.sase.tr (staging) and sase.tr (prod) BOTH run NODE_ENV=production with SEPARATE databases, so the previous NODE_ENV check would have let the hourly backfill sweep run against the dev DB too. Gate on the canonical prod host instead (COOLIFY_FQDN / BETTER_AUTH_URL), with an explicit CATALOG_BACKFILL_ENABLED override. Default off for any unknown host. New isCatalogBackfillEnabled() helper used by both the cron registration and processBackfillScan; dev redeploy now removes the stale scheduler. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Inject, Module, type OnModuleDestroy, type OnModuleInit } from "@nestjs/common";
|
||||
import { Queue } from "bullmq";
|
||||
import { CategoriesModule } from "../categories/categories.module";
|
||||
import { isCatalogBackfillEnabled } from "./prefetch-utils";
|
||||
import { PrefetchWorkerService } from "./prefetch-worker.service";
|
||||
import {
|
||||
CATALOG_PREFETCH_QUEUE,
|
||||
@@ -92,8 +93,9 @@ export class JobsModule implements OnModuleInit, OnModuleDestroy {
|
||||
// Catalog backfill: every hour at :00. Scans for decoded vehicles whose catalog
|
||||
// isn't fully prefetched and queues them. Self-throttled (queue-depth guard),
|
||||
// cooldown- and business-hours-aware — handled by PrefetchWorkerService.
|
||||
// PRODUCTION ONLY: dev runs against a separate DB and must not sweep/scrape.
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
// PRODUCTION (sase.tr) ONLY: dev.sase.tr is a separate DB and must not
|
||||
// sweep/scrape. NODE_ENV is "production" on BOTH, so gate on the prod host.
|
||||
if (isCatalogBackfillEnabled()) {
|
||||
await this.catalogPrefetchQueue.upsertJobScheduler(
|
||||
"catalog-backfill-hourly",
|
||||
{ pattern: "0 * * * *" },
|
||||
@@ -110,7 +112,7 @@ export class JobsModule implements OnModuleInit, OnModuleDestroy {
|
||||
} else {
|
||||
// Clean up any stale scheduler (e.g. if NODE_ENV changed) and stay idle.
|
||||
await this.catalogPrefetchQueue.removeJobScheduler("catalog-backfill-hourly").catch(() => {});
|
||||
console.log("[jobs] Skipped catalog-backfill cron (NODE_ENV != production)");
|
||||
console.log("[jobs] Skipped catalog-backfill cron (not prod host)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
import { RedisService } from "../redis/redis.service";
|
||||
|
||||
/**
|
||||
* True ONLY on the real production deployment (sase.tr).
|
||||
*
|
||||
* NOTE: both dev.sase.tr (staging) and sase.tr (prod) run with
|
||||
* `NODE_ENV=production` (hard-coded in the Dockerfile / compose) but have
|
||||
* SEPARATE databases. So NODE_ENV cannot tell them apart. The catalog backfill
|
||||
* must only sweep the prod DB, so we key off the canonical prod host. An
|
||||
* explicit `CATALOG_BACKFILL_ENABLED` env wins (e.g. to force-test on staging).
|
||||
* Anything else (local dev, unknown host) → disabled, the safe default.
|
||||
*/
|
||||
export function isCatalogBackfillEnabled(): boolean {
|
||||
const flag = process.env.CATALOG_BACKFILL_ENABLED;
|
||||
if (flag != null && flag !== "") return flag === "true";
|
||||
return (
|
||||
process.env.COOLIFY_FQDN === "sase.tr" || process.env.BETTER_AUTH_URL === "https://sase.tr"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom error that tells BullMQ to retry after a delay.
|
||||
* The worker catches this and re-queues the job with the specified delay.
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
checkCooldown,
|
||||
checkTimeWindow,
|
||||
initProgress,
|
||||
isCatalogBackfillEnabled,
|
||||
isWithinTimeWindow,
|
||||
updateProgress,
|
||||
} from "./prefetch-utils";
|
||||
@@ -293,10 +294,11 @@ export class PrefetchWorkerService implements OnModuleInit, OnModuleDestroy {
|
||||
* business-hours window. Goal: no decoded vehicle is left without catalog data.
|
||||
*/
|
||||
private async processBackfillScan(): Promise<void> {
|
||||
// Production only — dev uses a separate DB and must never sweep/scrape.
|
||||
// Defense-in-depth in case a scan job lands here via a shared Redis/queue.
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
this.logger.log("[backfill] Skip — NODE_ENV != production");
|
||||
// Production (sase.tr) only — dev.sase.tr uses a separate DB and must never
|
||||
// sweep/scrape. Defense-in-depth in case a scan job lands here via a stale
|
||||
// scheduler. NODE_ENV is "production" on both envs, so gate on the prod host.
|
||||
if (!isCatalogBackfillEnabled()) {
|
||||
this.logger.log("[backfill] Skip — not prod host");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user