google-cloud-platformnext.jsgoogle-cloud-firestoregoogle-cloud-run

Temporary staled cached page in Next.js App returned after serverless cold start


I have a Next.js app deployed to GCP Cloud Run as a serverless website.

Its home page is a server component of a list of "Cards". It fetches data of the "Cards" from Firestore and renders it to the page like this:

// src/app/page.tsx
export const revalidate = 900;

const getRecentCards = cache(async (): Promise<Card[]> => {
  try {
    // fetch cards from Firestore
    // return a list of cards
  } catch (error) {
    return [];
  }
});

export default async function HomePage() {
  const cards = await getRecentCards();
  // display the cards
  return (...{cards.map(card => {<div>...</div>} )}...)

Since the cards are generated by infrequent schduled cronjob so I set the revaldidate to 900 seconds (15 mins).

And I deploy the app to Cloud Run by running gcloud run deploy --source ..

I am expecting the behavior to be something like this:

  1. When user#1 comes to the home page, one server is started and since this is the first request (on this physical server), the server component fetches the "current" list of Cards (1, 2, 3), builds the static html file and returns it to the user. Since this is a cold start and server component fetches data, it will be slow;
  2. Shortly, when user#2 comes to the home page, because my configuration of "revalidate" is 900 seconds, Next.js server should directly return the cached page with card 1, 2, 3. This should be quick.
  3. After 15 mins, but before serverless system "pausing" the server, when user#3 comes to the home page, since it exceeds 900 seconds, the server will fetch Firestore again to revalidate the cache and return 1 more new card: 1, 2, 3, 4.
  4. After a little bit longer, the serverless system "pauses" the server. When user#4 comes to the home page, it triggers a cold start, then fetch the Firestore, and user#4 should observe cards: 1, 2, 3, 4.

However I am observing different behavior for user#4:

  1. When the Next.js is deployed, let's say at the build time there are 3 Cards: 1, 2, 3.
  2. User #1, #2, #3 will observe the same as expected. However user#4 will first get cards 1, 2, 3. Then wait for a while, like 20 secs, user#4 refreshes the page to get Card 1, 2, 3, 4.

This will happen all the time in future, let's say now we have Cards 1-10 in the Firestore, whenever there is a cold start, user will receive (1, 2, 3) then wait for a while then refresh to get the whole list of cards.

It feels like there is a "build-time cache" freezed in the "cold start image". My question is how do I disable this "build-time cache". Or if I misunderstood anything. Thnk you.


Solution

  • During a cold start, Cloud Run loads the last built image which seems to include the built time cache. You can disable caching in your Next.js app so that it always fetches the latest data. For example, you can set the cache option to no-store in a fetch request. However, this might lead to higher costs since Firestore charges for every read operation.