Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@devkiran
Copy link
Collaborator

@devkiran devkiran commented Dec 19, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Improved partner bulk deletion to ensure complete removal of associated messages and bounty submissions, preventing orphaned data.
  • Chores

    • Optimized partner deletion operations for improved performance.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Contributor

vercel bot commented Dec 19, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
dub Ready Ready Preview Dec 19, 2025 10:30am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 19, 2025

Walkthrough

Optimized bulk partner deletion logic by caching programEnrollmentIds array and reusing it across multiple Prisma queries. Added new deletion steps for messages and bounty submissions associated with program enrollments, improving code reusability and scope of deletions.

Changes

Cohort / File(s) Change Summary
Partner Bulk Deletion Optimization
apps/web/lib/api/partners/bulk-delete-partners.ts
Cached programEnrollmentIds array to eliminate repeated mapping; added deletion logic for messages and bounty submissions; refactored Prisma queries to reuse cached array across commissions, payouts, and program enrollments deletion paths.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Verify that the cached programEnrollmentIds array is correctly scoped and reused across all deletion queries
  • Confirm that new message and bounty submission deletions follow proper cascade patterns and don't orphan related data
  • Check edge cases where programEnrollments may be empty

Possibly related PRs

  • #3226: Modifies the same bulk-delete-partners file and refactors programEnrollment-based deletion logic; this PR extends that work with caching optimization and additional deletion cascades.

Suggested reviewers

  • steven-tey

Poem

🐰 A rabbit hops through deletion flows,
With cached IDs, efficiency grows,
Messages and bounties swept away,
Bulk deletion cleaner every day! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main addition in the changeset: deletion of bounty submissions in the bulkDeletePartners function, along with performance improvements through caching.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-cleanup-cron

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/web/lib/api/partners/bulk-delete-partners.ts (1)

105-127: Consider batching messages and bounty submissions deletions for consistency.

While the new deletions correctly clean up messages and bounty submissions, they don't use batching unlike the commissions deletion (lines 68-92). For consistency and to future-proof against potential scalability issues with large datasets, consider applying the same batching pattern used for commissions.

🔎 Example batching pattern for messages and bounty submissions

Apply batching similar to commissions:

-  // Delete the messages
-  const deletedMessages = await prisma.message.deleteMany({
-    where: {
-      programEnrollment: {
-        id: {
-          in: programEnrollmentIds,
-        },
-      },
-    },
-  });
-  console.log(`Deleted ${deletedMessages.count} messages`);
+  // Delete the messages
+  while (true) {
+    const messagesToDelete = await prisma.message.findMany({
+      where: {
+        programEnrollment: {
+          id: {
+            in: programEnrollmentIds,
+          },
+        },
+      },
+      take: BATCH_SIZE,
+    });
+
+    if (messagesToDelete.length === 0) {
+      break;
+    }
+
+    const deletedMessages = await prisma.message.deleteMany({
+      where: {
+        id: {
+          in: messagesToDelete.map((message) => message.id),
+        },
+      },
+    });
+    console.log(`Deleted ${deletedMessages.count} messages`);
+  }

-  // Delete the bounty submissions
-  const deletedBountySubmissions = await prisma.bountySubmission.deleteMany({
-    where: {
-      programEnrollment: {
-        id: {
-          in: programEnrollmentIds,
-        },
-      },
-    },
-  });
-  console.log(`Deleted ${deletedBountySubmissions.count} bounty submissions`);
+  // Delete the bounty submissions
+  while (true) {
+    const bountySubmissionsToDelete = await prisma.bountySubmission.findMany({
+      where: {
+        programEnrollment: {
+          id: {
+            in: programEnrollmentIds,
+          },
+        },
+      },
+      take: BATCH_SIZE,
+    });
+
+    if (bountySubmissionsToDelete.length === 0) {
+      break;
+    }
+
+    const deletedBountySubmissions = await prisma.bountySubmission.deleteMany({
+      where: {
+        id: {
+          in: bountySubmissionsToDelete.map((bs) => bs.id),
+        },
+      },
+    });
+    console.log(`Deleted ${deletedBountySubmissions.count} bounty submissions`);
+  }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d0b4fb and a6bd637.

📒 Files selected for processing (1)
  • apps/web/lib/api/partners/bulk-delete-partners.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: TWilson023
Repo: dubinc/dub PR: 3098
File: apps/web/lib/actions/partners/message-program.ts:49-58
Timestamp: 2025-11-12T22:23:10.414Z
Learning: In apps/web/lib/actions/partners/message-program.ts, when checking if a partner can continue messaging after messaging is disabled, the code intentionally requires `senderPartnerId: null` (program-initiated messages) to prevent partners from continuing to send junk messages. Only conversations started by the program should continue after messaging is disabled, as a spam prevention mechanism.
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/lib/actions/partners/update-partner-profile.ts:97-104
Timestamp: 2025-09-24T15:57:55.285Z
Learning: In the Dub codebase, the team prefers to maintain consistency with existing patterns for database operations, even if there are theoretical improvements available. The deleteMany: {} pattern for relation updates is an accepted approach that the team considers low-risk.
📚 Learning: 2025-12-03T09:19:48.164Z
Learnt from: devkiran
Repo: dubinc/dub PR: 3175
File: apps/web/lib/actions/partners/bulk-reject-partner-applications.ts:14-21
Timestamp: 2025-12-03T09:19:48.164Z
Learning: In apps/web/lib/actions/partners/bulk-reject-partner-applications.ts, the bulkRejectPartnerApplicationsAction does not need explicit plan capability checks for fraud operations (when reportFraud is true) because the authorization is handled automatically by the underlying fraud operation functions (resolveFraudGroups, createFraudEvents) or through other automated mechanisms in the system.

Applied to files:

  • apps/web/lib/api/partners/bulk-delete-partners.ts
📚 Learning: 2025-11-12T22:23:10.414Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 3098
File: apps/web/lib/actions/partners/message-program.ts:49-58
Timestamp: 2025-11-12T22:23:10.414Z
Learning: In apps/web/lib/actions/partners/message-program.ts, when checking if a partner can continue messaging after messaging is disabled, the code intentionally requires `senderPartnerId: null` (program-initiated messages) to prevent partners from continuing to send junk messages. Only conversations started by the program should continue after messaging is disabled, as a spam prevention mechanism.

Applied to files:

  • apps/web/lib/api/partners/bulk-delete-partners.ts
📚 Learning: 2025-09-17T17:44:03.965Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2857
File: apps/web/lib/actions/partners/update-program.ts:96-0
Timestamp: 2025-09-17T17:44:03.965Z
Learning: In apps/web/lib/actions/partners/update-program.ts, the team prefers to keep the messagingEnabledAt update logic simple by allowing client-provided timestamps rather than implementing server-controlled timestamp logic to avoid added complexity.

Applied to files:

  • apps/web/lib/api/partners/bulk-delete-partners.ts
📚 Learning: 2025-06-06T07:59:03.120Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.

Applied to files:

  • apps/web/lib/api/partners/bulk-delete-partners.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
apps/web/lib/api/partners/bulk-delete-partners.ts (2)

29-29: LGTM! Good optimization.

Caching the program enrollment IDs avoids repeated mapping operations and improves code readability.


73-73: LGTM! Consistent use of cached IDs.

The consistent use of programEnrollmentIds across all queries improves maintainability and eliminates duplicate mapping operations.

Also applies to: 98-98, 143-143

Comment on lines +105 to +127
// Delete the messages
const deletedMessages = await prisma.message.deleteMany({
where: {
programEnrollment: {
id: {
in: programEnrollments.map((pe) => pe.id),
in: programEnrollmentIds,
},
},
},
});
console.log(`Deleted ${deletedMessages.count} messages`);

// Delete the bounty submissions
const deletedBountySubmissions = await prisma.bountySubmission.deleteMany({
where: {
programEnrollment: {
id: {
in: programEnrollmentIds,
},
},
},
});
console.log(`Deleted ${deletedBountySubmissions.count} bounty submissions`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n apps/web/lib/api/partners/bulk-delete-partners.ts | head -150

Repository: dubinc/dub

Length of output: 4791


🏁 Script executed:

fd -e prisma

Repository: dubinc/dub

Length of output: 1223


🏁 Script executed:

rg -n 'programEnrollment' --type ts -B 2 -A 2 | head -100

Repository: dubinc/dub

Length of output: 6732


🏁 Script executed:

rg -n 'programEnrollment' packages/prisma/schema/ -A 3 -B 1

Repository: dubinc/dub

Length of output: 4613


🏁 Script executed:

# Also search for models that might reference programEnrollment indirectly or through relations
rg -n 'ProgramEnrollment' packages/prisma/schema/ --type prisma

Repository: dubinc/dub

Length of output: 84


🏁 Script executed:

# Check if DiscountCode is being deleted elsewhere or has cascade rules
rg -n 'discountCode\|DiscountCode' apps/web/lib/api/partners/bulk-delete-partners.ts

Repository: dubinc/dub

Length of output: 36


🏁 Script executed:

# Check full DiscountCode schema definition
cat -n packages/prisma/schema/discount.prisma

Repository: dubinc/dub

Length of output: 1909


🏁 Script executed:

# Check FraudCheck schema to confirm cascade behavior
cat -n packages/prisma/schema/fraud.prisma

Repository: dubinc/dub

Length of output: 3311


DiscountCode records must be deleted to prevent orphaned foreign keys.

The deletions are incomplete. The code deletes links (line 56-65), but DiscountCode has a required foreign key to Link without cascading deletion. This will leave orphaned DiscountCode records after links are deleted. Additionally, DiscountCode references programEnrollment optionally, which is also being deleted. Add explicit deletion of DiscountCode records that reference the programs being cleaned up.

🤖 Prompt for AI Agents
In apps/web/lib/api/partners/bulk-delete-partners.ts around lines 105 to 127,
you must also remove DiscountCode records to avoid FK constraint/orphan issues:
add explicit deleteMany calls for DiscountCode that target records referencing
the programEnrollmentIds (and any linkIds you remove) before deleting
Links/ProgramEnrollments; in practice run prisma.discountCode.deleteMany({
where: { OR: [{ programEnrollmentId: { in: programEnrollmentIds } }, { linkId: {
in: linkIds } }] } }) (or separate deletes) prior to deleting links/enrollments
so required foreign keys are not left orphaned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants