Skip to content

Commit 2bdd6df

Browse files
roaekeithwillcodezomars
authored
avoid calendar cache on cold starts (#8311)
Co-authored-by: Keith Williams <[email protected]> Co-authored-by: Omar López <[email protected]>
1 parent ad958f1 commit 2bdd6df

File tree

3 files changed

+68
-27
lines changed

3 files changed

+68
-27
lines changed

packages/core/CalendarManager.ts

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type { CredentialPayload, CredentialWithAppName } from "@calcom/types/Cre
1919
import type { EventResult } from "@calcom/types/EventManager";
2020

2121
const log = logger.getChildLogger({ prefix: ["CalendarManager"] });
22+
let coldStart = true;
2223

2324
export const getCalendarCredentials = (credentials: Array<CredentialPayload>) => {
2425
const calendarCredentials = getApps(credentials)
@@ -183,48 +184,71 @@ export const getCachedResults = async (
183184
*/
184185
const getNextCache = async (username: string, month: string): Promise<EventBusyDate[][]> => {
185186
let localCache: EventBusyDate[][] = [];
187+
const { NODE_ENV } = process.env;
188+
const cacheDir = `${NODE_ENV === "development" ? NODE_ENV : process.env.BUILD_ID}`;
189+
const baseUrl = `${WEBAPP_URL}/_next/data/${cacheDir}/en`;
190+
const url = `${baseUrl}/${username}/calendar-cache/${month}.json?user=${username}&month=${month}`;
186191
try {
187-
const { NODE_ENV } = process.env;
188-
const cacheDir = `${NODE_ENV === "development" ? NODE_ENV : process.env.BUILD_ID}`;
189-
const baseUrl = `${WEBAPP_URL}/_next/data/${cacheDir}/en`;
190-
console.log(`${baseUrl}/${username}/calendar-cache/${month}.json?user=${username}&month=${month}`);
191-
localCache = await fetch(
192-
`${baseUrl}/${username}/calendar-cache/${month}.json?user=${username}&month=${month}`
193-
)
192+
localCache = await fetch(url)
194193
.then((r) => r.json())
195194
.then((json) => json?.pageProps?.results);
196-
// No need to wait for this, the purpose is to force re-validation every second as indicated
197-
// in page getStaticProps.
198-
fetch(`${baseUrl}/${username}/calendar-cache/${month}`).catch(console.log);
199195
} catch (e) {
200-
log.warn(e);
196+
log.warn(url, e);
201197
}
202198
return localCache;
203199
};
200+
/**
201+
* Get months between given dates
202+
* @returns ["2023-04", "2024-05"]
203+
*/
204+
const getMonths = (dateFrom: string, dateTo: string): string[] => {
205+
const months: string[] = [dayjs(dateFrom).format("YYYY-MM")];
206+
for (
207+
let i = 1;
208+
dayjs(dateFrom).add(i, "month").isBefore(dateTo) ||
209+
dayjs(dateFrom).add(i, "month").isSame(dateTo, "month");
210+
i++
211+
) {
212+
months.push(dayjs(dateFrom).add(i, "month").format("YYYY-MM"));
213+
}
214+
return months;
215+
};
204216

217+
const createCalendarCachePage = (username: string, month: string): void => {
218+
// No need to wait for this, the purpose is to force re-validation every second as indicated
219+
// in page getStaticProps.
220+
fetch(`${WEBAPP_URL}/${username}/calendar-cache/${month}`).catch(console.log);
221+
};
205222
export const getBusyCalendarTimes = async (
206223
username: string,
207224
withCredentials: CredentialPayload[],
208225
dateFrom: string,
209-
dateTo: string
226+
dateTo: string,
227+
selectedCalendars: SelectedCalendar[]
210228
) => {
211229
let results: EventBusyDate[][] = [];
212-
if (dayjs(dateFrom).isSame(dayjs(dateTo), "month")) {
213-
results = await getNextCache(username, dayjs(dateFrom).format("YYYY-MM"));
214-
} else {
215-
// if dateFrom and dateTo is from different months get cache by each month
216-
const months: string[] = [dayjs(dateFrom).format("YYYY-MM")];
217-
for (
218-
let i = 1;
219-
dayjs(dateFrom).add(i, "month").isBefore(dateTo) ||
220-
dayjs(dateFrom).add(i, "month").isSame(dateTo, "month");
221-
i++
222-
) {
223-
months.push(dayjs(dateFrom).add(i, "month").format("YYYY-MM"));
230+
const months = getMonths(dateFrom, dateTo);
231+
try {
232+
if (coldStart) {
233+
results = await getCachedResults(withCredentials, dateFrom, dateTo, selectedCalendars);
234+
logger.info("Generating calendar cache in background");
235+
// on cold start the calendar cache page generated in the background
236+
Promise.all(months.map((month) => createCalendarCachePage(username, month)));
237+
} else {
238+
if (months.length === 1) {
239+
results = await getNextCache(username, dayjs(dateFrom).format("YYYY-MM"));
240+
} else {
241+
// if dateFrom and dateTo is from different months get cache by each month
242+
const data: EventBusyDate[][][] = await Promise.all(
243+
months.map((month) => getNextCache(username, month))
244+
);
245+
results = data.flat(1);
246+
}
224247
}
225-
const data: EventBusyDate[][][] = await Promise.all(months.map((month) => getNextCache(username, month)));
226-
results = data.flat(1);
248+
} catch (e) {
249+
logger.warn(e);
227250
}
251+
coldStart = false;
228252
return results.reduce((acc, availability) => acc.concat(availability), []);
229253
};
230254

packages/core/getBusyTimes.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import dayjs from "@calcom/dayjs";
66
import logger from "@calcom/lib/logger";
77
import { performance } from "@calcom/lib/server/perfObserver";
88
import prisma from "@calcom/prisma";
9+
import type { SelectedCalendar } from "@calcom/prisma/client";
910
import type { EventBusyDetails } from "@calcom/types/Calendar";
1011

1112
export async function getBusyTimes(params: {
@@ -17,6 +18,7 @@ export async function getBusyTimes(params: {
1718
beforeEventBuffer?: number;
1819
afterEventBuffer?: number;
1920
endTime: string;
21+
selectedCalendars: SelectedCalendar[];
2022
}) {
2123
const {
2224
credentials,
@@ -27,6 +29,7 @@ export async function getBusyTimes(params: {
2729
endTime,
2830
beforeEventBuffer,
2931
afterEventBuffer,
32+
selectedCalendars,
3033
} = params;
3134
logger.silly(
3235
`Checking Busy time from Cal Bookings in range ${startTime} to ${endTime} for input ${JSON.stringify({
@@ -119,7 +122,20 @@ export async function getBusyTimes(params: {
119122
performance.mark("prismaBookingGetEnd");
120123
performance.measure(`prisma booking get took $1'`, "prismaBookingGetStart", "prismaBookingGetEnd");
121124
if (credentials?.length > 0) {
122-
const calendarBusyTimes = await getBusyCalendarTimes(username, credentials, startTime, endTime);
125+
const startConnectedCalendarsGet = performance.now();
126+
const calendarBusyTimes = await getBusyCalendarTimes(
127+
username,
128+
credentials,
129+
startTime,
130+
endTime,
131+
selectedCalendars
132+
);
133+
const endConnectedCalendarsGet = performance.now();
134+
logger.debug(
135+
`Connected Calendars get took ${
136+
endConnectedCalendarsGet - startConnectedCalendarsGet
137+
} ms for user ${username}`
138+
);
123139
busyTimes.push(
124140
...calendarBusyTimes.map((value) => ({
125141
...value,

packages/core/getUserAvailability.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ export async function getUserAvailability(
149149
username: `${user.username}`,
150150
beforeEventBuffer,
151151
afterEventBuffer,
152+
selectedCalendars: user.selectedCalendars,
152153
});
153154

154155
let bufferedBusyTimes: EventBusyDetails[] = busyTimes.map((a) => ({

0 commit comments

Comments
 (0)