From dfe64ef4f581ae1c893dff0f0d4aeff34a251260 Mon Sep 17 00:00:00 2001 From: Evan Bonsignori Date: Mon, 10 Feb 2025 10:07:08 -0800 Subject: [PATCH] lessen the stress to our backend from the api/cookies call (#54324) Co-authored-by: Hector Alfaro --- src/events/components/dotcom-cookies.ts | 59 ++++++++++++++++--------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/src/events/components/dotcom-cookies.ts b/src/events/components/dotcom-cookies.ts index 4f8decc768eb..d92f7288ef51 100644 --- a/src/events/components/dotcom-cookies.ts +++ b/src/events/components/dotcom-cookies.ts @@ -8,37 +8,42 @@ type DotcomCookies = { let cachedCookies: DotcomCookies | null = null let inFlightPromise: Promise | null = null -let tries = 0 const GET_COOKIES_ENDPOINT = '/api/cookies' -const MAX_TRIES = 3 +const LOCAL_STORAGE_KEY = 'dotcomCookies' -// Fetches httpOnly cookies from the server and cache the result -// We use an in-flight promise to avoid duplicate requests +// Fetches httpOnly cookies from the server and caches the result. +// We don't want to do this every time because of the load it would place on our servers +// So on success, the data is stored in local storage and reused on subsequent loads +// On failure, returns default empty values +// If a user is staff and they didn't happen to be logged in when these cookies were saved, +// we can instruct them as needed to update the cookies and correctly set the isStaff flag. async function fetchCookies(): Promise { + // Return the cached object if we have it in memory. if (cachedCookies) { return cachedCookies } - // If request is already in progress, return the same promise - if (inFlightPromise) { - return inFlightPromise - } - - if (tries > MAX_TRIES) { - // In prod, fail without a serious error - console.error('Failed to fetch cookies after 3 tries') - // In dev, be loud about the issue - if (process.env.NODE_ENV === 'development') { - throw new Error('Failed to fetch cookies after 3 tries') + // Try to load from local storage. + const storedCookies = localStorage.getItem(LOCAL_STORAGE_KEY) + if (storedCookies) { + try { + cachedCookies = JSON.parse(storedCookies) as DotcomCookies + return cachedCookies + } catch (e) { + console.error('Error parsing cookies from local storage:', e) + localStorage.removeItem(LOCAL_STORAGE_KEY) } + } - return Promise.resolve({}) + // If a request is already in progress, reuse it. + if (inFlightPromise) { + return inFlightPromise } + // Make a single fetch request to the backend. inFlightPromise = fetch(GET_COOKIES_ENDPOINT) .then((response) => { - tries++ if (!response.ok) { throw new Error(`Failed to fetch cookies: ${response.statusText}`) } @@ -46,12 +51,26 @@ async function fetchCookies(): Promise { }) .then((data) => { cachedCookies = data + // Store the fetched cookies in local storage for future use. + try { + localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(data)) + } catch (e) { + console.error('Error storing cookies in local storage:', e) + } return data }) + .catch((err) => { + console.error('Error fetching cookies:', err) + // On failure, return default values. + const defaultCookies: DotcomCookies = { + dotcomUsername: '', + isStaff: false, + } + cachedCookies = defaultCookies + return defaultCookies + }) .finally(() => { - // Clear the in-flight promise regardless of success or failure - // On success, subsequent calls will return the cached value - // On failure, subsequent calls will retry the request up to MAX_TRIES times + // Clear the in-flight promise regardless of success or failure. inFlightPromise = null })