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 <me@alexvanandel.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 <keithwillcode@gmail.com> Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
This commit is contained in:
co-authored by
cubic-dev-ai[bot]
Devin AI
Keith Williams
Anik Dhabal Babu
parent
573756eaa2
commit
0b73491367
+57
-39
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user