-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Improve commissions import efficiency with getLeadEvents #2705
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 Git ↗︎
|
WalkthroughThis update introduces batched fetching of customer and lead event data in the commission import process, refactors related functions to accept pre-fetched datasets, modularizes the Tinybird lead events pipe into a reusable library, and adjusts the backfill script to use this shared module. Additionally, a new Tinybird pipe is added for lead events, and an email template URL is updated. Changes
Sequence Diagram(s)sequenceDiagram
participant Importer as importCommissions
participant DB as Prisma DB
participant TB as Tinybird
participant Creator as createCommission
Importer->>DB: Fetch customers by stripeCustomerId (batch)
Importer->>TB: getLeadEvents for customerIds (batch)
loop For each commission in batch
Importer->>Creator: Pass commission, customersData, customerLeadEvents
Creator->>Creator: Lookup customer & leadEvent in prefetched data
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~15 minutes Poem
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
@CodeRabbit please do a final review |
|
@steven-tey I'll conduct a final review of the PR focusing on the commission import efficiency improvements and the getLeadEvents implementation. ✅ Actions performedReview triggered.
|
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: 0
🧹 Nitpick comments (1)
apps/web/lib/tinybird/get-lead-events.ts (1)
1-11: Well-structured reusable module for lead events fetching.The implementation correctly extracts the Tinybird pipe construction into a shared module, promoting code reuse and maintainability. The use of Zod validation for both parameters and data ensures type safety.
Consider adding JSDoc documentation to improve developer experience:
+/** + * Fetches lead events from Tinybird for the specified customer IDs. + * @param customerIds - Array of customer IDs to fetch lead events for + * @returns Promise resolving to lead events data + */ export const getLeadEvents = tb.buildPipe({ pipe: "get_lead_events", parameters: z.object({ customerIds: z.string().array(), }), data: leadEventSchemaTB, });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/lib/rewardful/import-commissions.ts(7 hunks)apps/web/lib/tinybird/get-lead-events.ts(1 hunks)apps/web/scripts/backfill-customers.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: in apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward conditi...
Learnt from: TWilson023
PR: dubinc/dub#2673
File: apps/web/ui/partners/rewards/rewards-logic.tsx:268-275
Timestamp: 2025-07-30T15:29:54.131Z
Learning: In apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward condition, dependent fields (attribute, operator, value) should be reset rather than preserved because different entities (customer vs sale) have different available attributes. Maintaining existing fields when the entity changes would create invalid state combinations and confusing UX.
Applied to files:
apps/web/lib/rewardful/import-commissions.ts
📚 Learning: in the partnerprogramsummary email template (packages/email/src/templates/partner-program-summary.ts...
Learnt from: devkiran
PR: dubinc/dub#2448
File: packages/email/src/templates/partner-program-summary.tsx:0-0
Timestamp: 2025-05-29T04:45:18.504Z
Learning: In the PartnerProgramSummary email template (packages/email/src/templates/partner-program-summary.tsx), the stat titles are hardcoded constants ("Clicks", "Leads", "Sales", "Earnings") that will always match the ICONS object keys after toLowerCase() conversion, so icon lookup failures are not possible.
Applied to files:
apps/web/lib/rewardful/import-commissions.ts
📚 Learning: in apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined res...
Learnt from: devkiran
PR: dubinc/dub#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/rewardful/import-commissions.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 (6)
apps/web/scripts/backfill-customers.ts (1)
5-5: Clean refactor to use the shared getLeadEvents module.The import statement correctly references the new shared module, eliminating code duplication while maintaining the existing functionality of the backfill script.
apps/web/lib/rewardful/import-commissions.ts (5)
5-5: Appropriate imports for the batch processing refactor.The new imports support the performance optimization:
Customer,Linktypes for the prefetched data structuresgetLeadEventsfrom the new shared moduleLeadEventTBtype for the lead events arrayAlso applies to: 9-9, 12-12
55-70: Excellent performance optimization with batch data fetching.This change eliminates the N+1 query problem by:
- Prefetching all customers for the current batch of commissions
- Bulk fetching lead events for all customers at once
- Passing the prefetched data to avoid repeated queries
The implementation correctly extracts Stripe customer IDs from commissions, filters out null values, and includes the related link data needed downstream.
138-139: Updated function signature to support batch processing.The
createCommissionfunction now accepts the prefetched datasets as parameters, enabling in-memory lookups instead of individual database queries. The type annotations correctly reflect the data structures with included relations.Also applies to: 146-147
250-253: Efficient in-memory lookup replaces database query.The customer lookup now uses the prefetched
customersDataarray instead of a Prisma query, significantly improving performance by eliminating database round trips.
303-305: Streamlined lead event processing with prefetched data.The changes correctly:
- Use array lookup instead of API call for lead events (lines 303-305)
- Simplify the existence check by removing the data length validation (line 307)
- Parse the lead event object directly instead of accessing
data[0](line 321)This maintains the same validation logic while leveraging the batch-fetched data for better performance.
Also applies to: 307-307, 321-321
|
@CodeRabbit final review like your life depends on it |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chainI'll conduct an extremely thorough review of every aspect of this efficiency improvement. Let me examine the actual implementation details to ensure everything is bulletproof. 🏁 Script executed: Length of output: 13318 |
|
@coderabbitai final review please |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 12343 |
|
@coderabbitai final review please |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 11641 |
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: 0
🧹 Nitpick comments (1)
pipes/get_lead_events.pipe (1)
3-13: Consider query optimization for better performance.The SQL query structure is solid with proper parameterization and conditional logic. However, consider these optimizations:
- Column selection: Using
SELECT *might be inefficient if not all columns are needed. Consider selecting only required columns.- Result limiting: Without a LIMIT clause, this could return very large datasets for customers with many lead events.
Consider this optimization if only specific columns are needed:
- SELECT * + SELECT customer_id, timestamp, event_type, /* other required columns */ FROM dub_lead_eventsOr add pagination support:
ORDER BY timestamp DESC + {% if defined(limit) %} LIMIT {{ Int32(limit, 1000) }} {% end %}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/email/src/templates/program-imported.tsx(1 hunks)pipes/get_lead_events.pipe(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/email/src/templates/program-imported.tsx
⏰ 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)
pipes/get_lead_events.pipe (2)
1-2: LGTM! Token definition follows best practices.The read-only token is appropriately scoped for a data fetching endpoint.
14-17: LGTM! Proper endpoint type definition.The endpoint type is correctly specified for HTTP access, and the file structure follows Tinybird conventions.
|
@CodeRabbit final review please, you didn't provide one before |
|
No description provided. |
|
@CodeRabbit full review |
✅ Actions performedFull review triggered. |
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 (5)
apps/web/lib/rewardful/import-commissions.ts(7 hunks)apps/web/lib/tinybird/get-lead-events.ts(1 hunks)apps/web/scripts/backfill-customers.ts(1 hunks)packages/email/src/templates/program-imported.tsx(1 hunks)pipes/get_lead_events.pipe(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: in apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward conditi...
Learnt from: TWilson023
PR: dubinc/dub#2673
File: apps/web/ui/partners/rewards/rewards-logic.tsx:268-275
Timestamp: 2025-07-30T15:29:54.131Z
Learning: In apps/web/ui/partners/rewards/rewards-logic.tsx, when setting the entity field in a reward condition, dependent fields (attribute, operator, value) should be reset rather than preserved because different entities (customer vs sale) have different available attributes. Maintaining existing fields when the entity changes would create invalid state combinations and confusing UX.
Applied to files:
apps/web/lib/rewardful/import-commissions.ts
📚 Learning: in the partnerprogramsummary email template (packages/email/src/templates/partner-program-summary.ts...
Learnt from: devkiran
PR: dubinc/dub#2448
File: packages/email/src/templates/partner-program-summary.tsx:0-0
Timestamp: 2025-05-29T04:45:18.504Z
Learning: In the PartnerProgramSummary email template (packages/email/src/templates/partner-program-summary.tsx), the stat titles are hardcoded constants ("Clicks", "Leads", "Sales", "Earnings") that will always match the ICONS object keys after toLowerCase() conversion, so icon lookup failures are not possible.
Applied to files:
apps/web/lib/rewardful/import-commissions.ts
📚 Learning: in apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined res...
Learnt from: devkiran
PR: dubinc/dub#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/rewardful/import-commissions.ts
🧬 Code Graph Analysis (1)
apps/web/lib/tinybird/get-lead-events.ts (1)
apps/web/lib/zod/schemas/leads.ts (1)
leadEventSchemaTB(87-96)
🔇 Additional comments (8)
packages/email/src/templates/program-imported.tsx (1)
66-66: LGTM! API path restructuring implemented correctly.The addition of "workspaces" to the download URL path aligns with the broader API restructuring mentioned in the PR objectives.
pipes/get_lead_events.pipe (1)
1-16: LGTM! Well-structured Tinybird pipe definition.The pipe correctly implements:
- Proper token definition with READ permissions
- Conditional filtering by customerIds array using Tinybird's templating syntax
- Appropriate ordering by timestamp DESC for recent events first
- Clean SQL structure that selects all columns from dub_lead_events
apps/web/scripts/backfill-customers.ts (1)
5-5: LGTM! Clean refactor to use shared module.The extraction of
getLeadEventsfrom inline definition to a shared module promotes code reuse and maintainability, while keeping the same functionality.apps/web/lib/tinybird/get-lead-events.ts (1)
1-11: LGTM! Well-implemented type-safe Tinybird pipe wrapper.The module correctly implements:
- Proper pipe name matching the Tinybird pipe definition
- Type-safe parameter validation with Zod schema for customerIds array
- Appropriate data schema using
leadEventSchemaTB- Clean module structure following established patterns
apps/web/lib/rewardful/import-commissions.ts (4)
250-253: LGTM! Efficient in-memory customer lookup.The replacement of individual Prisma queries with in-memory lookups using the pre-fetched
customersDataarray significantly improves performance by eliminating N+1 database queries.
303-305: LGTM! Efficient in-memory lead event lookup.The replacement of individual
getLeadEventcalls with in-memory lookups using the pre-fetchedcustomerLeadEventsarray eliminates N+1 API calls and improves performance significantly.
307-321: LGTM! Correct handling of lead event data structure.The logic correctly handles the lead event lookup and parsing:
- Proper existence check for
leadEvent- Updated parsing to use
leadEventdirectly instead ofleadEvent.data- Maintains existing validation and error handling flow
55-70: Add robust error and edge‐case handling around batch fetchPlease update lines 55–70 of
apps/web/lib/rewardful/import-commissions.tsto guard against failures and empty results when fetching customers and lead events:
- Wrap both
prisma.customer.findManyandgetLeadEventsin atry/catchso one bad batch won’t crash the entire import loop.- Before calling
getLeadEvents, check for an emptycustomersDataarray and skip or short-circuit that batch.- Validate that the response from
getLeadEventsactually has adataproperty before using it.Suggested diff:
@@ apps/web/lib/rewardful/import-commissions.ts - const customersData = await prisma.customer.findMany({ + try { + const customersData = await prisma.customer.findMany({ where: { stripeCustomerId: { in: commissions .map((commission) => commission.sale.referral.stripe_customer_id) .filter(Boolean), }, }, include: { link: true }, - const customerLeadEvents = await getLeadEvents({ - customerIds: customersData.map((customer) => customer.id), - }).then((res) => res.data); + }); + + if (customersData.length === 0) { + // No matching customers; skip this batch + continue; + } + + const response = await getLeadEvents({ + customerIds: customersData.map((c) => c.id), + }); + + if (!response?.data) { + throw new Error('Unexpected getLeadEvents response'); + } + + const customerLeadEvents = response.data; + } catch (error) { + console.error(`Batch fetch failed at page ${currentPage}:`, error); + // move on to next batch without halting the import + continue; + }Please verify that:
- Your loop variables (
currentPage,processedBatches, etc.) still align with this flow.getLeadEventsalways returns an object with adatafield in all success cases.
Summary by CodeRabbit
Performance Improvements
Refactor
Bug Fixes