refactor: Fix N+1 queries and optimize database operations (#25813)

* refactor: prisma N+1 Queries

* addressed review
This commit is contained in:
Anik Dhabal Babu
2025-12-15 14:14:19 +00:00
committed by GitHub
parent 5626bceeff
commit 7562ad2a16
5 changed files with 119 additions and 66 deletions
@@ -47,7 +47,9 @@ export async function handler(req: NextRequest) {
if (isSendgridEnabled) {
const remindersToDelete: { referenceId: string | null; id: number }[] = await getAllRemindersToDelete();
const reminderIds: number[] = [];
const handlePastCancelledReminders = remindersToDelete.map(async (reminder) => {
reminderIds.push(reminder.id);
try {
if (reminder.referenceId) {
await deleteScheduledSend(reminder.referenceId);
@@ -55,19 +57,25 @@ export async function handler(req: NextRequest) {
} catch (err) {
logger.error(`Error deleting scheduled send (ref: ${reminder.referenceId}): ${err}`);
}
try {
await prisma.workflowReminder.update({
where: { id: reminder.id },
data: { referenceId: null },
});
} catch (err) {
logger.error(`Error updating reminder (id: ${reminder.id}): ${err}`);
}
});
await Promise.allSettled(handlePastCancelledReminders);
if (reminderIds.length > 0) {
try {
await prisma.workflowReminder.updateMany({
where: {
id: {
in: reminderIds,
},
},
data: { referenceId: null },
});
} catch (err) {
logger.error(`Error updating reminders: ${err}`);
}
}
//cancel reminders for cancelled/rescheduled bookings that are scheduled within the next hour
const remindersToCancel: { referenceId: string | null; id: number }[] = await getAllRemindersToCancel();
@@ -89,12 +97,11 @@ export async function handler(req: NextRequest) {
cancelUpdatePromises.push(cancelPromise, updatePromise);
}
Promise.allSettled(cancelUpdatePromises).then((results) => {
results.forEach((result) => {
if (result.status === "rejected") {
logger.error(`Error cancelling scheduled_sends: ${result.reason}`);
}
});
const results = await Promise.allSettled(cancelUpdatePromises);
results.forEach((result) => {
if (result.status === "rejected") {
logger.error(`Error cancelling scheduled_sends: ${result.reason}`);
}
});
}
@@ -490,12 +497,11 @@ export async function handler(req: NextRequest) {
}
}
Promise.allSettled(sendEmailPromises).then((results) => {
results.forEach((result) => {
if (result.status === "rejected") {
logger.error("Email sending failed", result.reason);
}
});
const sendResults = await Promise.allSettled(sendEmailPromises);
sendResults.forEach((result) => {
if (result.status === "rejected") {
logger.error("Email sending failed", result.reason);
}
});
return NextResponse.json({ message: `${unscheduledReminders.length} Emails to schedule` }, { status: 200 });
@@ -49,6 +49,31 @@ export class WorkflowReminderRepository {
});
}
static async findWorkflowRemindersByStepIds(workflowStepIds: number[]) {
if (workflowStepIds.length === 0) {
return [];
}
return await prisma.workflowReminder.findMany({
where: {
workflowStepId: {
in: workflowStepIds,
},
},
select: {
id: true,
referenceId: true,
method: true,
workflowStepId: true,
booking: {
select: {
eventTypeId: true,
},
},
},
});
}
static async findWorkflowReminderForAIPhoneCallExecution(id: number) {
const bookingSelect = {
uid: true,
@@ -118,31 +118,27 @@ export const getEventTypesByViewer = async (user: User, filters?: Filters, forRo
const mapEventType = async (eventType: UserEventTypes) => {
const userRepo = new UserRepository(prisma);
const eventTypeUsers = eventType?.hosts?.length
? eventType.hosts.map((host) => host.user)
: eventType.users;
const enrichedUsers = await userRepo.enrichUsersWithTheirProfiles(eventTypeUsers);
const children = eventType.children || [];
const allChildUsers = children.flatMap((c) => c.users);
const enrichedAllChildUsers = await userRepo.enrichUsersWithTheirProfiles(allChildUsers);
const enrichedUsersMap = new Map(enrichedAllChildUsers.map((user) => [user.id, user]));
const enrichedChildren = children.map((c) => ({
...c,
users: c.users.map((user) => enrichedUsersMap.get(user.id)).filter((user) => !!user),
}));
return {
...eventType,
safeDescription: eventType?.description ? markdownToSafeHTML(eventType.description) : undefined,
users: await Promise.all(
(eventType?.hosts?.length ? eventType?.hosts.map((host) => host.user) : eventType.users).map(
async (u) =>
await userRepo.enrichUserWithItsProfile({
user: u,
})
)
),
users: enrichedUsers,
metadata: eventType.metadata ? eventTypeMetaDataSchemaWithUntypedApps.parse(eventType.metadata) : null,
children: await Promise.all(
(eventType.children || []).map(async (c) => ({
...c,
users: await Promise.all(
c.users.map(
async (u) =>
await userRepo.enrichUserWithItsProfile({
user: u,
})
)
),
}))
),
children: enrichedChildren,
};
};
@@ -430,34 +430,44 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
},
});
await Promise.all(
hostGroups.map(async (group) => {
await ctx.prisma.hostGroup.upsert({
where: { id: group.id },
update: { name: group.name },
create: {
const existingGroupsMap = new Map(existingHostGroups.map((group) => [group.id, group]));
const newGroupsMap = new Map(hostGroups.map((group) => [group.id, group]));
const groupsToCreate = hostGroups.filter((group) => !existingGroupsMap.has(group.id));
const groupsToUpdate = hostGroups.filter((group) => existingGroupsMap.has(group.id));
const groupsToDelete = existingHostGroups.filter((existingGroup) => !newGroupsMap.has(existingGroup.id));
await ctx.prisma.$transaction(async (tx) => {
// Create new groups
if (groupsToCreate.length > 0) {
await tx.hostGroup.createMany({
data: groupsToCreate.map((group) => ({
id: group.id,
name: group.name,
eventTypeId: id,
})),
});
}
// Update existing groups
for (const group of groupsToUpdate) {
await tx.hostGroup.update({
where: { id: group.id },
data: { name: group.name },
});
}
// Delete groups that are no longer in the new list
if (groupsToDelete.length > 0) {
await tx.hostGroup.deleteMany({
where: {
id: {
in: groupsToDelete.map((group) => group.id),
},
},
});
})
);
const newGroupsMap = new Map(hostGroups.map((group) => [group.id, group]));
// Delete groups that are no longer in the new list
const groupsToDelete = existingHostGroups.filter((existingGroup) => !newGroupsMap.has(existingGroup.id));
if (groupsToDelete.length > 0) {
await ctx.prisma.hostGroup.deleteMany({
where: {
id: {
in: groupsToDelete.map((group) => group.id),
},
},
});
}
}
});
}
if (teamId && hosts) {
@@ -253,6 +253,22 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
const agentRepo = new PrismaAgentRepository(prisma);
const stepIds = userWorkflow.steps.map((step) => step.id);
const allReminders = await WorkflowReminderRepository.findWorkflowRemindersByStepIds(stepIds);
const remindersByStepId = new Map<number, (typeof allReminders)[number][]>();
for (const reminder of allReminders) {
const stepId = reminder.workflowStepId;
if (stepId === null) continue;
let list = remindersByStepId.get(stepId);
if (!list) {
list = [];
remindersByStepId.set(stepId, list);
}
list.push(reminder);
}
// handle deleted and edited workflow steps
await Promise.all(
userWorkflow.steps.map(async (oldStep) => {
@@ -271,7 +287,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
};
}
const remindersFromStep = await WorkflowReminderRepository.findWorkflowRemindersByStepId(oldStep.id);
const remindersFromStep = remindersByStepId.get(oldStep.id) || [];
//step was deleted
if (!newStep) {
if (oldStep.action === WorkflowActions.CAL_AI_PHONE_CALL && !!oldStep.agentId) {