-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Fix merge partner accounts #3039
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. WalkthroughPerformed per-submission transfer of bounty submissions using Promise.allSettled, added logPromiseResults utility, invoked syncTotalCommissions per transferred program for the target partner, and wrapped user/partner deletions in try/catch with error logging. Changes
Sequence Diagram(s)sequenceDiagram
participant Route as MergeAccounts Cron Route
participant DB as Database
participant Tiny as Tinybird/Cache
participant Comms as syncTotalCommissions
participant Logger as Logger
participant PromAll as Promise.allSettled
rect rgb(235,243,255)
Note over Route: Update links / notifications / cache
Route->>DB: updateMany (links/notifications)
Route->>Tiny: update links / expire caches
end
rect rgb(220,255,230)
Note over Route: Bounty submission transfer (per-item)
Route->>DB: fetch bountySubmissions by sourcePartnerId & programIdsToTransfer
Route->>PromAll: update each submission -> set partnerId = targetPartnerId
PromAll->>DB: update each bountySubmission
PromAll->>Logger: return settled results
Logger->>Route: logPromiseResults(results)
Route->>Comms: syncTotalCommissions(targetPartnerId, programId) -- per program
Comms-->>Route: ack
end
rect rgb(255,240,235)
Note over Route: Deletions (non-fatal)
Route->>Logger: try delete user if workspaceCount === 0
alt error
Logger->>Logger: log error (continue)
end
Route->>Logger: try delete partner
alt error
Logger->>Logger: log error (continue)
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
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. Comment |
There was a problem hiding this 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
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts(2 hunks)packages/utils/src/functions/promises.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 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/app/(ee)/api/cron/merge-partner-accounts/route.ts
📚 Learning: 2025-08-14T05:17:51.825Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2735
File: apps/web/lib/actions/partners/delete-reward.ts:33-41
Timestamp: 2025-08-14T05:17:51.825Z
Learning: In the partner groups system, a rewardId can only belong to one group, establishing a one-to-one relationship between rewards and groups. This means using Prisma's `update` method (rather than `updateMany`) is appropriate when updating groups by rewardId.
Applied to files:
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.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/app/(ee)/api/cron/merge-partner-accounts/route.ts
🧬 Code graph analysis (1)
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts (2)
packages/prisma/index.ts (1)
prisma(3-9)apps/web/lib/storage.ts (1)
storage(251-251)
⏰ 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
| // Bounty submissions to transfer to the target partner | ||
| const bountySubmissions = await prisma.bountySubmission.findMany({ | ||
| where: { | ||
| programId: { | ||
| in: programIdsToTransfer, | ||
| }, | ||
| partnerId: sourcePartnerId, | ||
| }, | ||
| }); | ||
|
|
||
| // Attempting to update all source submissions to the target partnerId fails | ||
| // if the target already has submissions for the same bounties. | ||
| if (bountySubmissions.length > 0) { | ||
| await Promise.allSettled( | ||
| bountySubmissions.map((submission) => | ||
| prisma.bountySubmission.update({ | ||
| where: { | ||
| id: submission.id, | ||
| }, | ||
| data: { | ||
| partnerId: targetPartnerId, | ||
| }, | ||
| }), | ||
| ), | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle Promise.allSettled failures instead of ignoring them
Line 177: Switching to Promise.allSettled avoids hard failures, but we now discard the results entirely. Any rejected updates (for example, when the target already holds a submission for the same bounty) will be silently skipped while the route still reports a successful merge, leaving the source submission unmoved. Previously the single updateMany call failed atomically—now we can exit with inconsistent partner assignments and zero visibility.
Please inspect the settled results, log failures, and decide whether to retry or surface an error (e.g., by using the new logPromiseResults helper and acting on a non-zero failureCount). Without this, we can’t trust that bounty submissions actually moved.
🤖 Prompt for AI Agents
In apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts around lines 177
to 202, the Promise.allSettled results for updating bountySubmissions are
currently discarded which hides rejected updates; inspect the settled results,
call the existing logPromiseResults helper with a clear context to log errors,
then check the returned failureCount (or count rejected entries) and if > 0
either surface an error (throw/return a 5xx response) or implement a
retry/backoff for conflict cases — ensure the route does not report success when
any update failed.
There was a problem hiding this 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
♻️ Duplicate comments (1)
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts (1)
184-195: Handle rejected bounty transfers before reporting success
Promise.allSettledmakes the merge resilient, but we still drop every rejection on the floor. Any submission that fails to move (e.g., the target already owns that bounty) will silently stay on the source partner while the route claims success. Please inspect the settled results and fail fast (or implement a retry) if any updates were rejected, logging the reasons so we can act on them.- await Promise.allSettled( - bountySubmissions.map((submission) => - prisma.bountySubmission.update({ - where: { - id: submission.id, - }, - data: { - partnerId: targetPartnerId, - }, - }), - ), - ); + const submissionResults = await Promise.allSettled( + bountySubmissions.map((submission) => + prisma.bountySubmission.update({ + where: { id: submission.id }, + data: { partnerId: targetPartnerId }, + }), + ), + ); + + const failedSubmissions = submissionResults.filter( + (result): result is PromiseRejectedResult => + result.status === "rejected", + ); + + if (failedSubmissions.length > 0) { + failedSubmissions.forEach(({ reason }) => + console.error( + `[merge-partner-accounts] Failed to transfer bounty submission`, + reason, + ), + ); + throw new Error( + `Halting merge: ${failedSubmissions.length} bounty submissions were not transferred.`, + ); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts(3 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: devkiran
Repo: dubinc/dub PR: 2736
File: apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/[bountyId]/bounty-info.tsx:45-56
Timestamp: 2025-08-25T17:39:38.965Z
Learning: In the bounty system, each partner can only submit to the same bounty once. This means totalSubmissions (pending + approved + rejected) equals the number of unique partners who have submitted, making UI text like "X of Y partners completed" accurate when using totalSubmissions.
Learnt from: devkiran
Repo: dubinc/dub PR: 2833
File: apps/web/lib/actions/partners/approve-bounty-submission.ts:53-61
Timestamp: 2025-09-12T17:31:10.548Z
Learning: In approve-bounty-submission.ts, the logic `bounty.rewardAmount ?? rewardAmount` is intentional. Bounties with preset reward amounts should use those fixed amounts, and the rewardAmount override parameter is only used when bounty.rewardAmount is null/undefined (for custom reward bounties). This follows the design pattern where bounties are either "flat rate" (fixed amount) or "custom" (variable amount set during approval).
📚 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/app/(ee)/api/cron/merge-partner-accounts/route.ts
📚 Learning: 2025-08-26T14:32:33.851Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/lib/actions/partners/create-bounty-submission.ts:105-112
Timestamp: 2025-08-26T14:32:33.851Z
Learning: Non-performance bounties are required to have submissionRequirements. In create-bounty-submission.ts, it's appropriate to let the parsing fail if submissionRequirements is null for non-performance bounties, as this indicates a data integrity issue that should be caught.
Applied to files:
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts
📚 Learning: 2025-09-12T17:31:10.548Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2833
File: apps/web/lib/actions/partners/approve-bounty-submission.ts:53-61
Timestamp: 2025-09-12T17:31:10.548Z
Learning: In approve-bounty-submission.ts, the logic `bounty.rewardAmount ?? rewardAmount` is intentional. Bounties with preset reward amounts should use those fixed amounts, and the rewardAmount override parameter is only used when bounty.rewardAmount is null/undefined (for custom reward bounties). This follows the design pattern where bounties are either "flat rate" (fixed amount) or "custom" (variable amount set during approval).
Applied to files:
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.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/app/(ee)/api/cron/merge-partner-accounts/route.ts
📚 Learning: 2025-08-14T05:17:51.825Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2735
File: apps/web/lib/actions/partners/delete-reward.ts:33-41
Timestamp: 2025-08-14T05:17:51.825Z
Learning: In the partner groups system, a rewardId can only belong to one group, establishing a one-to-one relationship between rewards and groups. This means using Prisma's `update` method (rather than `updateMany`) is appropriate when updating groups by rewardId.
Applied to files:
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts
🧬 Code graph analysis (1)
apps/web/app/(ee)/api/cron/merge-partner-accounts/route.ts (4)
packages/prisma/index.ts (1)
prisma(3-9)apps/web/lib/tinybird/record-link.ts (1)
recordLink(76-93)apps/web/lib/api/partners/sync-total-commissions.ts (1)
syncTotalCommissions(5-50)apps/web/lib/storage.ts (1)
storage(251-251)
⏰ 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
Summary by CodeRabbit
New Features
Bug Fixes
Refactor