From 0b7349136771ae277ac481d2b01eae567cafa104 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Tue, 13 Jan 2026 14:56:46 +0000 Subject: [PATCH] refactor: split large org creation transaction into smaller batches (#25688) * refactor: split large org creation transaction into smaller batches The prisma.team.create call at L418 was creating the organization along with all orgProfiles and memberships in a single large transaction, which could timeout when there are many members. This change splits the operation into smaller, separate transactions: 1. Create the organization (team) with just metadata and organizationSettings 2. Create org profiles in batches of 50 using Promise.all 3. Create memberships in batches of 100 using createMany 4. Fetch created profiles to rebuild the member-profile mapping This approach avoids transaction timeouts while maintaining the same functionality and data integrity. Co-Authored-By: alex@cal.com * Apply suggestion from @cubic-dev-ai[bot] Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Co-authored-by: Keith Williams Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> --- scripts/seed.ts | 96 +++++++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 39 deletions(-) diff --git a/scripts/seed.ts b/scripts/seed.ts index 4bde00cca8..f53e49d47e 100644 --- a/scripts/seed.ts +++ b/scripts/seed.ts @@ -480,7 +480,7 @@ async function createOrganizationAndAddMembersAndTeams({ const { organizationSettings, ...restOrgData } = orgData; - // Create organization with those users as members + // Step 1: Create organization (team) with just metadata and organizationSettings const orgInDb = await prisma.team.create({ data: { ...restOrgData, @@ -490,55 +490,73 @@ async function createOrganizationAndAddMembersAndTeams({ : {}), isOrganization: true, }, - orgProfiles: { - create: orgMembersInDb.map((member) => ({ - uid: uuid(), - username: member.orgProfile.username, - movedFromUser: { - connect: { - id: member.id, - }, - }, - user: { - connect: { - id: member.id, - }, - }, - })), - }, organizationSettings: { create: { ...organizationSettings, }, }, - members: { - create: orgMembersInDb.map((member) => ({ - user: { - connect: { - id: member.id, - }, - }, - role: member.orgMembership.role || "MEMBER", - accepted: member.orgMembership.accepted, - })), - }, }, select: { id: true, - members: true, - orgProfiles: true, }, }); - const orgMembersInDBWithProfileId = await Promise.all( - orgMembersInDb.map(async (member) => ({ - ...member, - profile: { - ...member.orgProfile, - id: orgInDb.orgProfiles.find((p) => p.userId === member.id)?.id, - }, - })) - ); + // Step 2: Create org profiles in batches to avoid large transactions + const profileBatchSize = 50; + for (let i = 0; i < orgMembersInDb.length; i += profileBatchSize) { + const batch = orgMembersInDb.slice(i, i + profileBatchSize); + await Promise.all( + batch.map((member) => + prisma.profile.create({ + data: { + uid: uuid(), + username: member.orgProfile.username, + organizationId: orgInDb.id, + userId: member.id, + movedFromUser: { + connect: { + id: member.id, + }, + }, + }, + }) + ) + ); + } + + // Step 3: Create memberships using createMany for better performance + const membershipBatchSize = 100; + for (let i = 0; i < orgMembersInDb.length; i += membershipBatchSize) { + const batch = orgMembersInDb.slice(i, i + membershipBatchSize); + await prisma.membership.createMany({ + data: batch.map((member) => ({ + teamId: orgInDb.id, + userId: member.id, + role: member.orgMembership.role || "MEMBER", + accepted: member.orgMembership.accepted ?? false, + })), + }); + } + + // Step 4: Fetch created profiles to rebuild orgMembersInDBWithProfileId + const createdProfiles = await prisma.profile.findMany({ + where: { + organizationId: orgInDb.id, + userId: { in: orgMembersInDb.map((m) => m.id) }, + }, + select: { + id: true, + userId: true, + }, + }); + + const orgMembersInDBWithProfileId = orgMembersInDb.map((member) => ({ + ...member, + profile: { + ...member.orgProfile, + id: createdProfiles.find((p) => p.userId === member.id)?.id, + }, + })); // For each member create one event for (const member of orgMembersInDBWithProfileId) {