Browse Source

feat: add adIdStart argument to seed-ads script and implement ensureAdIdCounterAligned function

Dave 1 tháng trước cách đây
mục cha
commit
6cbf9b271f
1 tập tin đã thay đổi với 65 bổ sung3 xóa
  1. 65 3
      prisma/mongo/seed-ads.ts

+ 65 - 3
prisma/mongo/seed-ads.ts

@@ -4,6 +4,7 @@ import {
   ImageSource,
   Prisma,
 } from '@prisma/mongo/client';
+
 export function nowSecBigInt(): bigint {
   return BigInt(Math.floor(Date.now() / 1000));
 }
@@ -13,6 +14,7 @@ const prisma = new PrismaClient();
 type SeedArgs = {
   clean: boolean;
   perType: number;
+  adIdStart: number;
 };
 
 function parseArgs(argv: string[]): SeedArgs {
@@ -22,7 +24,7 @@ function parseArgs(argv: string[]): SeedArgs {
     process.env.SEED_CLEAN === '1' ||
     process.env.SEED_CLEAN === 'true';
 
-  const perTypeFromArg = (() => {
+  const perType = (() => {
     const idx = argv.findIndex((a) => a === '--perType');
     if (idx >= 0) {
       const v = Number(argv[idx + 1]);
@@ -33,7 +35,18 @@ function parseArgs(argv: string[]): SeedArgs {
     return 100;
   })();
 
-  return { clean, perType: perTypeFromArg };
+  const adIdStart = (() => {
+    const idx = argv.findIndex((a) => a === '--adIdStart');
+    if (idx >= 0) {
+      const v = Number(argv[idx + 1]);
+      if (Number.isFinite(v) && v > 0) return Math.floor(v);
+    }
+    const envValue = Number(process.env.SEED_ADID_START);
+    if (Number.isFinite(envValue) && envValue > 0) return Math.floor(envValue);
+    return 1;
+  })();
+
+  return { clean, perType, adIdStart };
 }
 
 function nowEpochSec(): bigint {
@@ -96,6 +109,39 @@ function makeTimeWindow(): { startDt: bigint; expiryDt: bigint } {
   return { startDt: BigInt(start), expiryDt: BigInt(expiry) };
 }
 
+/**
+ * Align the Mongo "counters" collection with the next adId we plan to use.
+ * This ensures future runtime allocator (findAndModify $inc) won't collide.
+ *
+ * We set counters.seq = maxExistingAdId (or adIdStart-1 if empty),
+ * so the next allocate will return max+1.
+ */
+async function ensureAdIdCounterAligned(adIdStart: number) {
+  const maxRow = await prisma.ads.findFirst({
+    where: { adId: { not: null } },
+    select: { adId: true },
+    orderBy: { adId: 'desc' as any }, // Prisma Mongo sometimes needs 'as any' for orderBy on optional field
+  });
+
+  const maxExisting =
+    typeof maxRow?.adId === 'number' ? maxRow.adId : undefined;
+
+  const base = Math.max((adIdStart ?? 1) - 1, maxExisting ?? 0);
+
+  await prisma.$runCommandRaw({
+    update: 'counters',
+    updates: [
+      {
+        q: { _id: 'ads_adId' },
+        u: { $set: { seq: base } },
+        upsert: true,
+      },
+    ],
+  });
+
+  console.log(`[seed:ads] counters.ads_adId aligned: seq=${base}`);
+}
+
 async function main() {
   const args = parseArgs(process.argv.slice(2));
   const tsNow = nowEpochSec();
@@ -106,6 +152,9 @@ async function main() {
     console.log('[seed:ads] cleaned ads collection');
   }
 
+  // Important: If your app uses the atomic allocator, align the counter to avoid collisions.
+  await ensureAdIdCounterAligned(args.adIdStart);
+
   const advertiserPrefixes = [
     '广告商A',
     '广告商B',
@@ -126,6 +175,8 @@ async function main() {
 
   const batch: Prisma.AdsCreateManyInput[] = [];
 
+  let nextAdId = args.adIdStart;
+
   for (const adType of adTypes) {
     for (let idx = 0; idx < args.perType; idx++) {
       const { startDt, expiryDt } = makeTimeWindow();
@@ -137,6 +188,7 @@ async function main() {
       const adsContent = Math.random() < 0.8 ? makeContent(adType) : null;
 
       batch.push({
+        adId: nextAdId++, // ✅ NEW
         adType,
         advertiser,
         title,
@@ -156,7 +208,7 @@ async function main() {
 
   const result = await prisma.ads.createMany({ data: batch });
   console.log(
-    `[seed:ads] adTypes=${adTypes.length} perType=${args.perType} inserted=${result.count} clean=${args.clean}`,
+    `[seed:ads] adTypes=${adTypes.length} perType=${args.perType} inserted=${result.count} clean=${args.clean} adIdStart=${args.adIdStart}`,
   );
 }
 
@@ -168,3 +220,13 @@ main()
   .finally(async () => {
     await prisma.$disconnect();
   });
+
+/**
+ * Usage:
+ *  pnpm ts-node ./path/to/seed-ads.ts --clean --perType 50 --adIdStart 1
+ *
+ * Env:
+ *  SEED_CLEAN=true
+ *  SEED_PER_TYPE=100
+ *  SEED_ADID_START=1
+ */