From 7900be3a305e9bb598ea67821e1a72610748ede0 Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Wed, 18 Jun 2025 02:53:04 -0400 Subject: [PATCH] perf: deduplicating DB fetches + leverage unique DB index for team type booking page (#21811) * wip * refactor * refactor * fix * optimize * better * comment * better code * do not use cache for now * simplify --- apps/web/lib/getTemporaryOrgRedirect.ts | 5 +- .../team/[slug]/[type]/getServerSideProps.tsx | 267 ++++++++++-------- packages/features/bookings/lib/get-booking.ts | 2 +- 3 files changed, 158 insertions(+), 116 deletions(-) diff --git a/apps/web/lib/getTemporaryOrgRedirect.ts b/apps/web/lib/getTemporaryOrgRedirect.ts index f2d443f17a..aa9b6cf824 100644 --- a/apps/web/lib/getTemporaryOrgRedirect.ts +++ b/apps/web/lib/getTemporaryOrgRedirect.ts @@ -3,6 +3,7 @@ import { stringify } from "querystring"; import logger from "@calcom/lib/logger"; import { safeStringify } from "@calcom/lib/safeStringify"; +import type { PrismaClient } from "@calcom/prisma"; import type { RedirectType } from "@calcom/prisma/client"; const log = logger.getSubLogger({ prefix: ["lib", "getTemporaryOrgRedirect"] }); @@ -11,13 +12,15 @@ export const getTemporaryOrgRedirect = async ({ redirectType, eventTypeSlug, currentQuery, + prismaClient, }: { slugs: string[] | string; redirectType: RedirectType; eventTypeSlug: string | null; currentQuery: ParsedUrlQuery; + prismaClient?: PrismaClient; }) => { - const prisma = (await import("@calcom/prisma")).default; + const prisma = prismaClient ?? (await import("@calcom/prisma")).default; slugs = slugs instanceof Array ? slugs : [slugs]; log.debug( `Looking for redirect for`, diff --git a/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx b/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx index 5a7a37ace9..835007460f 100644 --- a/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx +++ b/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx @@ -2,9 +2,11 @@ import type { GetServerSidePropsContext } from "next"; import { z } from "zod"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; -import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking"; import { getBookingForReschedule } from "@calcom/features/bookings/lib/get-booking"; -import { getSlugOrRequestedSlug, orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains"; +import { + orgDomainConfig, + whereClauseForOrgWithSlugOrRequestedSlug, +} from "@calcom/features/ee/organizations/lib/orgDomains"; import { getOrganizationSEOSettings } from "@calcom/features/ee/organizations/lib/orgSettings"; import { FeaturesRepository } from "@calcom/features/flags/features.repository"; import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; @@ -30,7 +32,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => const { req, params, query } = context; const session = await getServerSession({ req }); const { slug: teamSlug, type: meetingSlug } = paramsSchema.parse(params); - const { rescheduleUid, isInstantMeeting: queryIsInstantMeeting, email } = query; + const { rescheduleUid, isInstantMeeting: queryIsInstantMeeting } = query; const allowRescheduleForCancelledBooking = query.allowRescheduleForCancelledBooking === "true"; const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(req, params?.orgSlug); const isOrgContext = currentOrgDomain && isValidOrgDomain; @@ -41,6 +43,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => redirectType: RedirectType.Team, eventTypeSlug: meetingSlug, currentQuery: context.query, + prismaClient: prisma, }); if (redirect) { @@ -48,43 +51,49 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => } } - const team = await getTeamWithEventsData(teamSlug, meetingSlug, isValidOrgDomain, currentOrgDomain); + const [orgId, booking] = await Promise.all([ + isOrgContext ? getOrgId(currentOrgDomain) : Promise.resolve(null), + rescheduleUid ? getBookingForReschedule(`${rescheduleUid}`, session?.user?.id) : Promise.resolve(null), + ]); - if (!team || !team.eventTypes?.[0]) { + const team = await getTeamData(teamSlug, orgId); + + if (!team) { return { notFound: true } as const; } - const eventData = team.eventTypes[0]; + const eventData = await getEventTypeData(meetingSlug, team.id); + + if (!eventData) { + return { notFound: true } as const; + } if (rescheduleUid && eventData.disableRescheduling) { return { redirect: { destination: `/booking/${rescheduleUid}`, permanent: false } }; } + if ( + booking?.status === BookingStatus.CANCELLED && + !allowRescheduleForCancelledBooking && + !eventData.allowReschedulingCancelledBookings + ) { + return { + redirect: { + permanent: false, + destination: `/team/${teamSlug}/${meetingSlug}`, + }, + }; + } + const eventTypeId = eventData.id; - const eventHostsUserData = await getUsersData( - team.isPrivate, - eventTypeId, - eventData.hosts.map((h) => h.user) - ); const orgSlug = isValidOrgDomain ? currentOrgDomain : null; const name = team.parent?.name ?? team.name ?? null; - let booking: GetBookingType | null = null; - if (rescheduleUid) { - booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id); - if ( - booking?.status === BookingStatus.CANCELLED && - !allowRescheduleForCancelledBooking && - !eventData.allowReschedulingCancelledBookings - ) { - return { - redirect: { - permanent: false, - destination: `/team/${teamSlug}/${meetingSlug}`, - }, - }; - } - } + const eventHostsUserData = getEventHosts( + team.isPrivate, + eventData.hosts.map((h) => h.user), + eventData.users ?? [] + ); const fromRedirectOfNonOrgLink = context.query.orgRedirection === "true"; const isUnpublished = team.parent ? !team.parent.slug : !team.slug; @@ -95,11 +104,9 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => // Handle string[] type from query params let teamMemberEmail = Array.isArray(crmContactOwnerEmail) ? crmContactOwnerEmail[0] : crmContactOwnerEmail; - let crmOwnerRecordType = Array.isArray(crmContactOwnerRecordType) ? crmContactOwnerRecordType[0] : crmContactOwnerRecordType; - let crmAppSlug = Array.isArray(crmAppSlugParam) ? crmAppSlugParam[0] : crmAppSlugParam; if (!teamMemberEmail || !crmOwnerRecordType || !crmAppSlug) { @@ -122,7 +129,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => const organizationSettings = getOrganizationSEOSettings(team); const allowSEOIndexing = organizationSettings?.allowSEOIndexing ?? false; - const featureRepo = new FeaturesRepository(); const teamHasApiV2Route = await featureRepo.checkIfTeamHasFeature(team.id, "use-api-v2-for-team-slots"); const useApiV2 = teamHasApiV2Route && hasApiV2RouteInEnv(); @@ -172,116 +178,149 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => }; }; -const getTeamWithEventsData = async ( - teamSlug: string, - meetingSlug: string, - isValidOrgDomain: boolean, - currentOrgDomain: string | null -) => { +const getOrgId = async (orgSlug: string): Promise => { + const org = await prisma.team.findFirst({ + where: whereClauseForOrgWithSlugOrRequestedSlug(orgSlug), + select: { id: true }, + }); + + return org?.id ?? null; +}; + +const getTeamData = async (teamSlug: string, orgId: number | null) => { + const teamSelectFields = { + id: true, + isPrivate: true, + hideBranding: true, + logoUrl: true, + name: true, + slug: true, + isOrganization: true, + parent: { + select: { + slug: true, + name: true, + bannerUrl: true, + logoUrl: true, + hideBranding: true, + organizationSettings: { + select: { + allowSEOIndexing: true, + }, + }, + }, + }, + organizationSettings: { + select: { + allowSEOIndexing: true, + }, + }, + }; + + if (orgId !== null) { + const publishedTeam = await prisma.team.findUnique({ + where: { + slug_parentId: { + slug: teamSlug, + parentId: orgId, + }, + isOrganization: false, + }, + select: teamSelectFields, + }); + if (publishedTeam) return publishedTeam; + } + return await prisma.team.findFirst({ where: { - ...getSlugOrRequestedSlug(teamSlug), - parent: isValidOrgDomain && currentOrgDomain ? getSlugOrRequestedSlug(currentOrgDomain) : null, + parentId: orgId ?? null, + isOrganization: false, + OR: [ + { slug: teamSlug }, + { + metadata: { + path: ["requestedSlug"], + equals: teamSlug, + }, + }, + ], }, + select: teamSelectFields, orderBy: { slug: { sort: "asc", nulls: "last" }, }, + }); +}; + +const getEventTypeData = async (meetingSlug: string, teamId: number) => { + return await prisma.eventType.findUnique({ + where: { + // Use the EventType_teamId_slug_key unique index + teamId_slug: { + teamId: teamId, + slug: meetingSlug, + }, + }, select: { id: true, - isPrivate: true, - hideBranding: true, - parent: { + title: true, + isInstantEvent: true, + schedulingType: true, + metadata: true, + length: true, + hidden: true, + disableCancelling: true, + disableRescheduling: true, + allowReschedulingCancelledBookings: true, + interfaceLanguage: true, + hosts: { + take: 3, select: { - slug: true, + user: { + select: { + name: true, + username: true, + }, + }, + }, + }, + // Include users for when hosts is empty + users: { + take: 1, + select: { + username: true, name: true, - bannerUrl: true, - logoUrl: true, - hideBranding: true, - organizationSettings: { - select: { - allowSEOIndexing: true, - }, - }, - }, - }, - logoUrl: true, - name: true, - slug: true, - eventTypes: { - where: { - slug: meetingSlug, - }, - select: { - id: true, - title: true, - isInstantEvent: true, - schedulingType: true, - metadata: true, - length: true, - hidden: true, - disableCancelling: true, - disableRescheduling: true, - allowReschedulingCancelledBookings: true, - interfaceLanguage: true, - hosts: { - take: 3, - select: { - user: { - select: { - name: true, - username: true, - email: true, - }, - }, - }, - }, - }, - }, - isOrganization: true, - organizationSettings: { - select: { - allowSEOIndexing: true, }, }, }, }); }; -const getUsersData = async ( +const getEventHosts = ( isPrivateTeam: boolean, - eventTypeId: number, + hosts: Pick[], users: Pick[] ) => { - if (!isPrivateTeam && users.length > 0) { - return users + if (isPrivateTeam) { + return []; + } + + if (hosts.length > 0) { + return hosts .filter((user) => user.username) .map((user) => ({ username: user.username ?? "", name: user.name ?? "", })); } - if (!isPrivateTeam && users.length === 0) { - const { users: data } = await prisma.eventType.findUniqueOrThrow({ - where: { id: eventTypeId }, - select: { - users: { - take: 1, - select: { - username: true, - name: true, - }, - }, - }, - }); - return data.length > 0 - ? [ - { - username: data[0].username ?? "", - name: data[0].name ?? "", - }, - ] - : []; + if (users.length > 0) { + return [ + { + username: users[0].username ?? "", + name: users[0].name ?? "", + }, + ]; } return []; diff --git a/packages/features/bookings/lib/get-booking.ts b/packages/features/bookings/lib/get-booking.ts index ba44e23d07..92f754e662 100644 --- a/packages/features/bookings/lib/get-booking.ts +++ b/packages/features/bookings/lib/get-booking.ts @@ -156,7 +156,7 @@ export const getBookingForReschedule = async (uid: string, userId?: number) => { let attendeeEmail: string | null = null; let bookingSeatData: { description?: string; responses: Prisma.JsonValue } | null = null; if (!theBooking) { - const bookingSeat = await prisma.bookingSeat.findFirst({ + const bookingSeat = await prisma.bookingSeat.findUnique({ where: { referenceUid: uid, },