avoid calendar cache on cold starts (#8311)

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
This commit is contained in:
Efraín Rochín
2023-04-17 23:56:08 +00:00
committed by GitHub
co-authored by Keith Williams Omar López
parent ad958f10bd
commit 2bdd6df755
3 changed files with 68 additions and 27 deletions
+50 -26
View File
@@ -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<CredentialPayload>) => {
const calendarCredentials = getApps(credentials)
@@ -183,48 +184,71 @@ export const getCachedResults = async (
*/
const getNextCache = async (username: string, month: string): Promise<EventBusyDate[][]> => {
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), []);
};
+17 -1
View File
@@ -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,
+1
View File
@@ -149,6 +149,7 @@ export async function getUserAvailability(
username: `${user.username}`,
beforeEventBuffer,
afterEventBuffer,
selectedCalendars: user.selectedCalendars,
});
let bufferedBusyTimes: EventBusyDetails[] = busyTimes.map((a) => ({