-
Notifications
You must be signed in to change notification settings - Fork 27.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Incremental Static Regeneration (ISR) pages not re-rendering after revalidate expiry #24806
Labels
bug
Issue was opened via the bug report template.
Comments
tommarshall
added a commit
to kyan/next.js
that referenced
this issue
May 5, 2021
* As outlined in vercel#24806 I there's currently a bug with the incremental static regeneration (ISR) logic incorrectly prolonging the life of rendered page content when it's the `LRUCache` is refreshed with potentially stale content from the filesystem cache. * This because if a page is not present in the `LRUCache`, but is present in the filesystem cache then it's re-added to the `LRUCache` with a fresh cache expiry calculated from the current time using the `revalidate` window. * If that page continues to be pushed out of the LRUCache before the next request comes in after the revalidation window expires then this creates an endless loop of the same stale content from the filesystem being served, without it being regenerated. * This fix addresses that issue by adding a `fromTime` argument to the revalidation period calculation, so that when we're falling back to the filesystem we can use the filesystem's `lastModfied` timestamp for that file to anchor the revalidation calculation, rather than restarting from the current time in every case. * That means that if page content from the filesystem is stale it will be marked as so when returned from the incremental cache `get` method (`isStale: true`), which tells next server to regenerate the page in the background. * If however the the page content from the filesystem is still within the revalidate window (i.e. is not yet stale) then it will be added back to the LRUCache with the correct remaining duration for the `revalidateAfter`. Fixes vercel#24806
2 tasks
tommarshall
added a commit
to kyan/next.js
that referenced
this issue
May 5, 2021
* As outlined in vercel#24806 I there's currently a bug with the incremental static regeneration (ISR) logic incorrectly prolonging the life of rendered page content when it's the `LRUCache` is refreshed with potentially stale content from the filesystem cache. * This because if a page is not present in the `LRUCache`, but is present in the filesystem cache then it's re-added to the `LRUCache` with a fresh cache expiry calculated from the current time using the `revalidate` window. * If that page continues to be pushed out of the LRUCache before the next request comes in after the revalidation window expires then this creates an endless loop of the same stale content from the filesystem being served, without it being regenerated. * This fix addresses that issue by adding a `fromTime` argument to the revalidation period calculation, so that when we're falling back to the filesystem we can use the filesystem's `lastModfied` timestamp for that file to anchor the revalidation calculation, rather than restarting from the current time in every case. * That means that if page content from the filesystem is stale it will be marked as so when returned from the incremental cache `get` method (`isStale: true`), which tells next server to regenerate the page in the background. * If however the the page content from the filesystem is still within the revalidate window (i.e. is not yet stale) then it will be added back to the LRUCache with the correct remaining duration for the `revalidateAfter`. Fixes vercel#24806
tommarshall
added a commit
to kyan/next.js
that referenced
this issue
May 5, 2021
* As outlined in vercel#24806 I there's currently a bug with the incremental static regeneration (ISR) logic incorrectly prolonging the life of rendered page content when it's the `LRUCache` is refreshed with potentially stale content from the filesystem cache. * This because if a page is not present in the `LRUCache`, but is present in the filesystem cache then it's re-added to the `LRUCache` with a fresh cache expiry calculated from the current time using the `revalidate` window. * If that page continues to be pushed out of the LRUCache before the next request comes in after the revalidation window expires then this creates an endless loop of the same stale content from the filesystem being served, without it being regenerated. * This fix addresses that issue by adding a `fromTime` argument to the revalidation period calculation, so that when we're falling back to the filesystem we can use the filesystem's `lastModfied` timestamp for that file to anchor the revalidation calculation, rather than restarting from the current time in every case. * That means that if page content from the filesystem is stale it will be marked as so when returned from the incremental cache `get` method (`isStale: true`), which tells next server to regenerate the page in the background. * If however the the page content from the filesystem is still within the revalidate window (i.e. is not yet stale) then it will be added back to the LRUCache with the correct remaining duration for the `revalidateAfter`. Fixes vercel#24806
tommarshall
added a commit
to kyan/next.js
that referenced
this issue
May 5, 2021
* As outlined in vercel#24806 I there's currently a bug with the incremental static regeneration (ISR) logic incorrectly prolonging the life of rendered page content when it's the `LRUCache` is refreshed with potentially stale content from the filesystem cache. * This because if a page is not present in the `LRUCache`, but is present in the filesystem cache then it's re-added to the `LRUCache` with a fresh cache expiry calculated from the current time using the `revalidate` window. * If that page continues to be pushed out of the LRUCache before the next request comes in after the revalidation window expires then this creates an endless loop of the same stale content from the filesystem being served, without it being regenerated. * This fix addresses that issue by adding a `fromTime` argument to the revalidation period calculation, so that when we're falling back to the filesystem we can use the filesystem's `lastModfied` timestamp for that file to anchor the revalidation calculation, rather than restarting from the current time in every case. * That means that if page content from the filesystem is stale it will be marked as so when returned from the incremental cache `get` method (`isStale: true`), which tells next server to regenerate the page in the background. * If however the the page content from the filesystem is still within the revalidate window (i.e. is not yet stale) then it will be added back to the LRUCache with the correct remaining duration for the `revalidateAfter`. Fixes vercel#24806
tommarshall
added a commit
to kyan/next.js
that referenced
this issue
May 5, 2021
* As outlined in vercel#24806 I there's currently a bug with the incremental static regeneration (ISR) logic incorrectly prolonging the life of rendered page content when it's the `LRUCache` is refreshed with potentially stale content from the filesystem cache. * This because if a page is not present in the `LRUCache`, but is present in the filesystem cache then it's re-added to the `LRUCache` with a fresh cache expiry calculated from the current time using the `revalidate` window. * If that page continues to be pushed out of the LRUCache before the next request comes in after the revalidation window expires then this creates an endless loop of the same stale content from the filesystem being served, without it being regenerated. * This fix addresses that issue by adding a `fromTime` argument to the revalidation period calculation, so that when we're falling back to the filesystem we can use the filesystem's `lastModfied` timestamp for that file to anchor the revalidation calculation, rather than restarting from the current time in every case. * That means that if page content from the filesystem is stale it will be marked as so when returned from the incremental cache `get` method (`isStale: true`), which tells next server to regenerate the page in the background. * If however the the page content from the filesystem is still within the revalidate window (i.e. is not yet stale) then it will be added back to the LRUCache with the correct remaining duration for the `revalidateAfter`. Fixes vercel#24806
jamsinclair
pushed a commit
to jamsinclair/next.js
that referenced
this issue
Jul 20, 2021
* As outlined in vercel#24806 I there's currently a bug with the incremental static regeneration (ISR) logic incorrectly prolonging the life of rendered page content when it's the `LRUCache` is refreshed with potentially stale content from the filesystem cache. * This because if a page is not present in the `LRUCache`, but is present in the filesystem cache then it's re-added to the `LRUCache` with a fresh cache expiry calculated from the current time using the `revalidate` window. * If that page continues to be pushed out of the LRUCache before the next request comes in after the revalidation window expires then this creates an endless loop of the same stale content from the filesystem being served, without it being regenerated. * This fix addresses that issue by adding a `fromTime` argument to the revalidation period calculation, so that when we're falling back to the filesystem we can use the filesystem's `lastModfied` timestamp for that file to anchor the revalidation calculation, rather than restarting from the current time in every case. * That means that if page content from the filesystem is stale it will be marked as so when returned from the incremental cache `get` method (`isStale: true`), which tells next server to regenerate the page in the background. * If however the the page content from the filesystem is still within the revalidate window (i.e. is not yet stale) then it will be added back to the LRUCache with the correct remaining duration for the `revalidateAfter`. Fixes vercel#24806
2 tasks
kodiakhq bot
pushed a commit
that referenced
this issue
Jul 20, 2021
…27335) Fixes: #24806 Fixes: #26689 Fixes: #27325 Closes: #24807 @tommarshall has done us a huge favor with his PR #24807 which outlines exactly the issue and a pragmatic solution. I'm not trying to "steal their work", however, the PR seems to have been stuck for some months. I think there's huge value in this for myself and others as essentially **ISR is broken** for people running Next.js at scale 😱 This PR has cherry-picked @tommarshall's fine fix and added some integrations tests around page revalidation and the edge case when the cache size is exhausted. ✏️ Edits are enabled, so feel free great Vercel staff and other maintainers to improve my bad tests or surrounding code 🙇 ## Bug - [x] Related issues linked using `fixes #number` - [x] Integration tests added
Hi, this has been updated in |
flybayer
pushed a commit
to blitz-js/next.js
that referenced
this issue
Aug 19, 2021
…) (vercel#27335) Fixes: vercel#24806 Fixes: vercel#26689 Fixes: vercel#27325 Closes: vercel#24807 @tommarshall has done us a huge favor with his PR vercel#24807 which outlines exactly the issue and a pragmatic solution. I'm not trying to "steal their work", however, the PR seems to have been stuck for some months. I think there's huge value in this for myself and others as essentially **ISR is broken** for people running Next.js at scale 😱 This PR has cherry-picked @tommarshall's fine fix and added some integrations tests around page revalidation and the edge case when the cache size is exhausted. ✏️ Edits are enabled, so feel free great Vercel staff and other maintainers to improve my bad tests or surrounding code 🙇 ## Bug - [x] Related issues linked using `fixes #number` - [x] Integration tests added
This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you. |
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
What version of Next.js are you using?
10.0.5
What version of Node.js are you using?
12.20.0
What browser are you using?
Chrome & cURL
What operating system are you using?
macOS
How are you deploying your application?
next start on Azure App Service
Describe the Bug
Under certain circumstances the Incremental Static Regeneration (ISR) logic is incorrectly prolonging the life of rendered page content beyond the revalidation expiry, potentially indefinitely.
Those circumstances:
LRUCache
size.revalidate
period.For context, we're experiencing this on a site with:
LRUCache
item size (page size) of 420KB.LRUCache
at one time.revalidate
duration of300
(5 minutes).*This is the figure from analytics. We have quite a few pages listing large quantities of internal links at a time, which with
Next/Link
'sprefetch
behaviour amplifies this figure somewhat as far at the ISR cache is concerned.Next's incremental cache logic makes use of an in-memory
LRUCache
(least recently used cache) with a max size of 50MB, with a fallback to the filesystem for anything not found in theLRUCache
.I believe there's a bug in the incremental cache logic where if a page is not present in the
LRUCache
, then when falling back to the filesystem cache the (potentially) stale file content is added back to theLRUCache
for a full fresh revalidation period, which is a process that can repeat itself if the page keeps being pushed out of theLRUCache
before the next request comes in after the revalidation period expires.This creates the potential for stale content to be served in perpetuity for certain pages.
Expected Behavior
Under the stale-while-revalidate (SWR) caching model for the pages which make use of ISR, any request for a page which was last generated more than the defined
revalidate
seconds ago should trigger the page to be regenerated in the background.To Reproduce
Due to its nature, this is a tricky one to reproduce. You need a site with enough ISR pages (and a big enough page size) to exhaust the 50MB
LRUCache
space, and ideally some visibility into the cache state so that you can follow whether theLRUCache
is being hit, whether it's falling back to the filesystem, and when the cache is determining that a page is considered stale (should be regenerated bynext-server
).To that end I've created a branch which reduces the cache size to 1MB to make this easier to reproduce, and adds some logging to make this easier to follow, but it is possible to reproduce this without that branch, just harder. Setting the
revalidate
to a shorter period also makes life easier here. I found60
(1 min) a good balance, but I believe this bug is present for anyrevalidate
duration.rm -rf .next/
yarn build
yarn start
At this point the
LRUCache
is empty, and the filesystem cache is populated for any pages that are prebuilt during theyarn build
.LRUCache
, by requesting enough pages to exhaust the 50MB (Or 1MB if you're using theinvestigate-isr-pages-not-re-rendering-after-revalidate-expiry
branch) capacity./posts/foo-post
), and request it to bring it to the front of theLRUCache
.Next/Link
's prefetch behaviour can make life tricky here if you're using a browser, as it might sneakily request the test page if it's included as a link on one of the other pages.curl
is likely a safer way to go here.(See the
cacheKeys
log key for visibility of which pages are in theLRUCache
if using theinvestigate-isr-pages-not-re-rendering-after-revalidate-expiry
branch logging).revalidate
window to expire for the test page request (step 5), if it hasn't already..html
and.json
files within the filesystem cache directory, for example:.html
and.json
files again, e.g.Note that they have not changed. The page has not been regenerated, despite the revalidation window having expired.
LRUCache
again.LRUCache
.revalidate
window to expire for the most recent test page request (if it hasn't already)..html
and.json
files within the Next filesystem again, e.g..html
and.json
files again.Note that this time the last modified timestamps have updated. The page was regenerated because it was still in the
LRUCache
when the request came in following the expiry of the revalidation window.The text was updated successfully, but these errors were encountered: