From 585679969a0d325f6efeb2233765ad24446362b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Efra=C3=ADn=20Roch=C3=ADn?= Date: Tue, 6 Jun 2023 11:30:29 -0700 Subject: [PATCH] perf: Avoid useless dependencies in calendar cache page (#9290) * increases the time range to cover all time zones * it isolates the getCachedResults and rename to getCalendarsEvents * remove unrelated changes * remove unrelated changes --------- Co-authored-by: zomars --- .../pages/[user]/calendar-cache/[month].tsx | 6 +-- packages/core/CalendarManager.ts | 4 +- packages/core/getCalendarsEvents.ts | 51 +++++++++++++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 packages/core/getCalendarsEvents.ts diff --git a/apps/web/pages/[user]/calendar-cache/[month].tsx b/apps/web/pages/[user]/calendar-cache/[month].tsx index b8b4acb7d6..1bbd543c3d 100644 --- a/apps/web/pages/[user]/calendar-cache/[month].tsx +++ b/apps/web/pages/[user]/calendar-cache/[month].tsx @@ -6,13 +6,13 @@ import type { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from "next"; import { z } from "zod"; -import { getCachedResults } from "@calcom/core"; +import getCalendarsEvents from "@calcom/core/getCalendarsEvents"; import dayjs from "@calcom/dayjs"; import prisma from "@calcom/prisma"; const paramsSchema = z.object({ user: z.string(), month: z.string() }); export const getStaticProps: GetStaticProps< - { results: Awaited> }, + { results: Awaited> }, { user: string } > = async (context) => { const { user: username, month } = paramsSchema.parse(context.params); @@ -33,7 +33,7 @@ export const getStaticProps: GetStaticProps< const endDate = dayjs.utc(month, "YYYY-MM").endOf("month").add(14, "hours").format(); try { const results = userWithCredentials?.credentials - ? await getCachedResults( + ? await getCalendarsEvents( userWithCredentials?.credentials, startDate, endDate, diff --git a/packages/core/CalendarManager.ts b/packages/core/CalendarManager.ts index 47e8c265ec..02107d7001 100644 --- a/packages/core/CalendarManager.ts +++ b/packages/core/CalendarManager.ts @@ -18,6 +18,8 @@ import type { import type { CredentialPayload, CredentialWithAppName } from "@calcom/types/Credential"; import type { EventResult } from "@calcom/types/EventManager"; +import getCalendarsEvents from "./getCalendarsEvents"; + const log = logger.getChildLogger({ prefix: ["CalendarManager"] }); let coldStart = true; @@ -236,7 +238,7 @@ export const getBusyCalendarTimes = async ( const startDate = dayjs(dateFrom).subtract(11, "hours").format(); // Add 14 hours from the start date to avoid problems in UTC+ time zones. const endDate = dayjs(dateTo).endOf("month").add(14, "hours").format(); - results = await getCachedResults(withCredentials, startDate, endDate, selectedCalendars); + results = await getCalendarsEvents(withCredentials, startDate, endDate, selectedCalendars); logger.info("Generating calendar cache in background"); // on cold start the calendar cache page generated in the background Promise.all(months.map((month) => createCalendarCachePage(username, month))); diff --git a/packages/core/getCalendarsEvents.ts b/packages/core/getCalendarsEvents.ts new file mode 100644 index 0000000000..b00bdc674d --- /dev/null +++ b/packages/core/getCalendarsEvents.ts @@ -0,0 +1,51 @@ +import type { SelectedCalendar } from "@prisma/client"; + +import { getCalendar } from "@calcom/app-store/_utils/getCalendar"; +import { performance } from "@calcom/lib/server/perfObserver"; +import type { EventBusyDate } from "@calcom/types/Calendar"; +import type { CredentialPayload } from "@calcom/types/Credential"; + +const getCalendarsEvents = async ( + withCredentials: CredentialPayload[], + dateFrom: string, + dateTo: string, + selectedCalendars: SelectedCalendar[] +): Promise => { + const calendarCredentials = withCredentials.filter((credential) => credential.type.endsWith("_calendar")); + const calendars = await Promise.all(calendarCredentials.map((credential) => getCalendar(credential))); + performance.mark("getBusyCalendarTimesStart"); + const results = calendars.map(async (c, i) => { + /** Filter out nulls */ + if (!c) return []; + /** We rely on the index so we can match credentials with calendars */ + const { type, appId } = calendarCredentials[i]; + /** We just pass the calendars that matched the credential type, + * TODO: Migrate credential type or appId + */ + const passedSelectedCalendars = selectedCalendars.filter((sc) => sc.integration === type); + if (!passedSelectedCalendars.length) return []; + /** We extract external Ids so we don't cache too much */ + const selectedCalendarIds = passedSelectedCalendars.map((sc) => sc.externalId); + /** If we don't then we actually fetch external calendars (which can be very slow) */ + performance.mark("eventBusyDatesStart"); + const eventBusyDates = await c.getAvailability(dateFrom, dateTo, passedSelectedCalendars); + performance.mark("eventBusyDatesEnd"); + performance.measure( + `[getAvailability for ${selectedCalendarIds.join(", ")}][$1]'`, + "eventBusyDatesStart", + "eventBusyDatesEnd" + ); + + return eventBusyDates.map((a) => ({ ...a, source: `${appId}` })); + }); + const awaitedResults = await Promise.all(results); + performance.mark("getBusyCalendarTimesEnd"); + performance.measure( + `getBusyCalendarTimes took $1 for creds ${calendarCredentials.map((cred) => cred.id)}`, + "getBusyCalendarTimesStart", + "getBusyCalendarTimesEnd" + ); + return awaitedResults; +}; + +export default getCalendarsEvents;