diff --git a/packages/core/CalendarManager.ts b/packages/core/CalendarManager.ts index 4d3ee42cf1..af2c845655 100644 --- a/packages/core/CalendarManager.ts +++ b/packages/core/CalendarManager.ts @@ -19,6 +19,7 @@ import type { CredentialPayload, CredentialWithAppName } from "@calcom/types/Cre import type { EventResult } from "@calcom/types/EventManager"; const log = logger.getChildLogger({ prefix: ["CalendarManager"] }); +let coldStart = true; export const getCalendarCredentials = (credentials: Array) => { const calendarCredentials = getApps(credentials) @@ -183,48 +184,71 @@ export const getCachedResults = async ( */ const getNextCache = async (username: string, month: string): Promise => { let localCache: EventBusyDate[][] = []; + const { NODE_ENV } = process.env; + const cacheDir = `${NODE_ENV === "development" ? NODE_ENV : process.env.BUILD_ID}`; + const baseUrl = `${WEBAPP_URL}/_next/data/${cacheDir}/en`; + const url = `${baseUrl}/${username}/calendar-cache/${month}.json?user=${username}&month=${month}`; try { - const { NODE_ENV } = process.env; - const cacheDir = `${NODE_ENV === "development" ? NODE_ENV : process.env.BUILD_ID}`; - const baseUrl = `${WEBAPP_URL}/_next/data/${cacheDir}/en`; - console.log(`${baseUrl}/${username}/calendar-cache/${month}.json?user=${username}&month=${month}`); - localCache = await fetch( - `${baseUrl}/${username}/calendar-cache/${month}.json?user=${username}&month=${month}` - ) + localCache = await fetch(url) .then((r) => r.json()) .then((json) => json?.pageProps?.results); - // No need to wait for this, the purpose is to force re-validation every second as indicated - // in page getStaticProps. - fetch(`${baseUrl}/${username}/calendar-cache/${month}`).catch(console.log); } catch (e) { - log.warn(e); + log.warn(url, e); } return localCache; }; +/** + * Get months between given dates + * @returns ["2023-04", "2024-05"] + */ +const getMonths = (dateFrom: string, dateTo: string): string[] => { + const months: string[] = [dayjs(dateFrom).format("YYYY-MM")]; + for ( + let i = 1; + dayjs(dateFrom).add(i, "month").isBefore(dateTo) || + dayjs(dateFrom).add(i, "month").isSame(dateTo, "month"); + i++ + ) { + months.push(dayjs(dateFrom).add(i, "month").format("YYYY-MM")); + } + return months; +}; +const createCalendarCachePage = (username: string, month: string): void => { + // No need to wait for this, the purpose is to force re-validation every second as indicated + // in page getStaticProps. + fetch(`${WEBAPP_URL}/${username}/calendar-cache/${month}`).catch(console.log); +}; export const getBusyCalendarTimes = async ( username: string, withCredentials: CredentialPayload[], dateFrom: string, - dateTo: string + dateTo: string, + selectedCalendars: SelectedCalendar[] ) => { let results: EventBusyDate[][] = []; - if (dayjs(dateFrom).isSame(dayjs(dateTo), "month")) { - results = await getNextCache(username, dayjs(dateFrom).format("YYYY-MM")); - } else { - // if dateFrom and dateTo is from different months get cache by each month - const months: string[] = [dayjs(dateFrom).format("YYYY-MM")]; - for ( - let i = 1; - dayjs(dateFrom).add(i, "month").isBefore(dateTo) || - dayjs(dateFrom).add(i, "month").isSame(dateTo, "month"); - i++ - ) { - months.push(dayjs(dateFrom).add(i, "month").format("YYYY-MM")); + const months = getMonths(dateFrom, dateTo); + try { + if (coldStart) { + results = await getCachedResults(withCredentials, dateFrom, dateTo, 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))); + } else { + if (months.length === 1) { + results = await getNextCache(username, dayjs(dateFrom).format("YYYY-MM")); + } else { + // if dateFrom and dateTo is from different months get cache by each month + const data: EventBusyDate[][][] = await Promise.all( + months.map((month) => getNextCache(username, month)) + ); + results = data.flat(1); + } } - const data: EventBusyDate[][][] = await Promise.all(months.map((month) => getNextCache(username, month))); - results = data.flat(1); + } catch (e) { + logger.warn(e); } + coldStart = false; return results.reduce((acc, availability) => acc.concat(availability), []); }; diff --git a/packages/core/getBusyTimes.ts b/packages/core/getBusyTimes.ts index 5aad4eac59..42928970d3 100644 --- a/packages/core/getBusyTimes.ts +++ b/packages/core/getBusyTimes.ts @@ -6,6 +6,7 @@ import dayjs from "@calcom/dayjs"; import logger from "@calcom/lib/logger"; import { performance } from "@calcom/lib/server/perfObserver"; import prisma from "@calcom/prisma"; +import type { SelectedCalendar } from "@calcom/prisma/client"; import type { EventBusyDetails } from "@calcom/types/Calendar"; export async function getBusyTimes(params: { @@ -17,6 +18,7 @@ export async function getBusyTimes(params: { beforeEventBuffer?: number; afterEventBuffer?: number; endTime: string; + selectedCalendars: SelectedCalendar[]; }) { const { credentials, @@ -27,6 +29,7 @@ export async function getBusyTimes(params: { endTime, beforeEventBuffer, afterEventBuffer, + selectedCalendars, } = params; logger.silly( `Checking Busy time from Cal Bookings in range ${startTime} to ${endTime} for input ${JSON.stringify({ @@ -119,7 +122,20 @@ export async function getBusyTimes(params: { performance.mark("prismaBookingGetEnd"); performance.measure(`prisma booking get took $1'`, "prismaBookingGetStart", "prismaBookingGetEnd"); if (credentials?.length > 0) { - const calendarBusyTimes = await getBusyCalendarTimes(username, credentials, startTime, endTime); + const startConnectedCalendarsGet = performance.now(); + const calendarBusyTimes = await getBusyCalendarTimes( + username, + credentials, + startTime, + endTime, + selectedCalendars + ); + const endConnectedCalendarsGet = performance.now(); + logger.debug( + `Connected Calendars get took ${ + endConnectedCalendarsGet - startConnectedCalendarsGet + } ms for user ${username}` + ); busyTimes.push( ...calendarBusyTimes.map((value) => ({ ...value, diff --git a/packages/core/getUserAvailability.ts b/packages/core/getUserAvailability.ts index 942ec1b26a..7ba043dc02 100644 --- a/packages/core/getUserAvailability.ts +++ b/packages/core/getUserAvailability.ts @@ -149,6 +149,7 @@ export async function getUserAvailability( username: `${user.username}`, beforeEventBuffer, afterEventBuffer, + selectedCalendars: user.selectedCalendars, }); let bufferedBusyTimes: EventBusyDetails[] = busyTimes.map((a) => ({