-
-
Notifications
You must be signed in to change notification settings - Fork 457
[RFC] SSG with incremental-static-regeneration and revalidate #804
Description
Hi,
Is your feature request related to a problem? Please describe.
When you setup a page /[id].ts
for SSG with fallback: true
and an API route /api/preview.ts
to enable preview mode
, when you makes "preview" changes this works pretty well and you can see live changes of the page. 👍
Still, changes you make that appears correctly in the preview mode
does not update the s3 files static-pages/*.html _next/data/*/*.json
accordingly.
So whenever you display such a page "publicly" (with preview mode disabled), it does not display the latest changes. What it displays is the latest version of the page stored in s3.
Since currently, the preview mode does not update s3 files, we cannot see the changes with preview mode disabled.
So currently, for a specific page youdomain/abcde
, the file static-page/abcde.html
never updates (this is also true for the corresponding json file). When one makes changes in preview mode, the plugin does not update the static-pages abcde.html
so the change does not appear publicly.
How to reproduce the issue
To reproduce this problem, please follow the steps:
- Render without preview mode (no cookies) the page
/abced
. It should create_next/data/<id>abced.json
andstatic-pages/abced.html
files in s3 and render the page according to the RFC. 👍 - Modify the content of
/abced
with preview mode ON (with cookies) (to do this, use an API route, e.g./api/preview
). - Render again the page
/abced
, as the result, the content shows the updates make in 2/ 👍 - Remove the
preview mode cookies
- Display again the page
/abced
, since preview mode is OFF this time, the content are not the latest. It displays the result in 1/ instead of displaying the result with modified content in 3/ - If you look, steps 2. and 3. do not update files
_next/data/<id>abced.json
andstatic-pages/abced.html
in s3. It does not invalidate cloudfront cache neither.
Describe the solution you'd like
A good solution requires that when you make changes made in preview mode
, the next time you render without preview mode the page, it shows the changes.
To fix it, we need to make the fallback page never cached max-age: 0
, so the lambda can return the newly generated page (and cache it) the next time the same route is hit. But we also need to store the html and json files in preview mode for each preview request (RFC STEP 2)
Describe alternatives you've considered
The alternative is to force delete those files in s3 and to invalidate cloudfront cache without the plugin. It is not standard, does not follow the RFC and it is a ugly and unsatisfying solution. Per file cache invalidation is not scalable (limit of 3000 concurrent invalidation on AWS) and it has a cost.
Additional context
@dphang discusses a related issue here :
I did find that issue too, it is because fallback page gets cached after 1st hit. But if you bust the cache after hitting a non-prerendered route (add a random query parameter), with the new change it would pick up the page that was just stored in s3, so all props are populated.
To fix it, we need to make the fallback page never cached (
max-age 0
), so the lambda at the edges can return the newly generated page (and cache it) the next time the same route is hit. But we also need to version the pages properly as I realized we are not clearing them properly (we only have one set of pages under static-pages, so subsequent deploys may pick up an old version).
Originally posted by @dphang in #798 (comment)
This discussion triggers a related issue, that is users request cached, still up-to-date versions after a new deployment. This analysis apply to preview mode
too as described in this thread.
Also, #355
A.
Activity
[-]SSG with preview mode and fallback true does not update s3 files[/-][+][RFC proposal] SSG with preview mode and fallback true does not update s3 files[/+]arelaxend commentedon Nov 17, 2020
vercel/next.js#11552
#559
This would look something like:
Originally posted by @timneutkens in vercel/next.js#11552
https://github.com/vercel/next.js/blob/4866c47d9d9b39515d6e57118955e86c85630c4c/packages/next/next-server/server/incremental-cache.ts#L89
That one will be tricky to implement. We could discuss it separately after this RFC is completed.
Implementing
revalidate
is a tricky one I might have underestimated. In order to implement thestale-while-revalidate
spec properly you have to generate a fresh copy of the page in the background whilst serving the stale copy in a non-blocking fashion. Doing that in Lambda is not possible due the way the lambda execution environment works. In other words, Lambda doesn't let you return a response to the client and keep on running a task on the background. The closest thing to running something in the background is to setcallbackWaitsForEmptyEventLoop
to false but that freezes outstanding events so not even that would work.I need to take a closer look at Next.js implementation as I'm not sure if they conform strictly to the spec. Any ideas / suggestions please let me know 🙂
You're right @janus-reith. That removes the need for SQS even though I think AWS still uses a queue behind the scenes for async invocations, but if is abstracting it away from us that's best!
My only remaining concern would be latency between regions, say that a user is in us-east-1 and hits one of the edge functions in that region, when invoking the other lambda for background page regeneration what latency are we expected to see? I realise that it won't need to wait for the regeneration lambda to process but the request itself to put the event in the queue what region is that happening and how do we know is not going to a queue somewhere in
eu-*
.My initial guess would be the queue AWS uses behind the scenes would be colocated in the same region as the async lambda but I'm not sure of that.
Originally posted by @danielcondemarin in #355 (comment)
To fix it, we need to make the fallback page never cached, so the lambda can return the newly generated page (and cache it) the next time the same route is hit. But we also need to version the pages properly as I realized we are not clearing them properly (we only have one set of pages under static-pages, so subsequent deploys may pick up an old version).
Originally posted by @dphang in #798 (comment)
Also :
Can one adds this feature to the schedule ? This RFC is older than the v10, back to 9.4 👍
[-][RFC proposal] SSG with preview mode and fallback true does not update s3 files[/-][+][RFC] SSG with incremental-static-regeneration and revalidate[/+]krikork commentedon Dec 21, 2020
Support for incremental static regeneration would be very handy!
alexthewilde commentedon Dec 31, 2020
Hey @arelaxend, incremental static regeneration imho is a killer feature of Next.js. Do you have any idea if and by when serverless-next.js will support this?
vosinsky commentedon Feb 16, 2021
Hi, if there is an update on this? Or is there some other method to solve this problem?
evangow commentedon Feb 20, 2021
Spent some time thinking about this. My pseudocode is below for anyone that might have time to implement this. This covers both the setting { revalidation: Int } on getStaticProps and { fallback: "blocking" } on getStaticPaths (neither or which are currently handled from my understanding).
For example, if you get 100 requests for a particular page immediately after the revalidation period, the async lambda could get called all 100 times if the first time it was fired the function wasn't able to upload the new page to S3 before the next 99 requests hit
The concurrency limit on a lambda can't be set based on the function arguments as far as I'm aware though, so this would create a bottleneck for ALL page regeneration requests. If you've got dozens or hundreds of ISR pages with and a "revalidation: 1" second, then, you could be calling this lambda more frequently then it could process requests and upload them to S3, which would create an ever-expanding backlog of request
For this reason, I think it's better to perhaps let the 99 follow on requests (from the example) simply regenerate the page
https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/using-messagededuplicationid-property.html
In this case, the deduplication id would be composed of the lastModifiedField returned by the s3.headObject().promise() along with the path that the request was made on (e.g. /slug/...).
That way, the first request would insert the message into the SQS queue and the other 99 request would automatically be deduplicated.
The SQS queue would have the generatePageAndSaveToS3 lambda attached to process the queue request.
Based on my understanding of the docs, the deduplication id lasts for 5 mins, so as long as the generatePageAndSaveToS3 can complete the initial request within the 5 minute timeframe, then it should only ever get called once.
fallback: 'blocking'
#833avisra commentedon Mar 23, 2021
Just confirming my understanding here - incremental static regeneration does not currently work with this repo?
GuillaumeSD commentedon Mar 23, 2021
Hi @avisra
From what I know and do, SSG does not yet work with this repo. Discovered this when I saw the revalidate option didn't work with getStaticProps.
SSG is a killer feature imho, really looking forward to it.
avisra commentedon Mar 24, 2021
well... this is a real bummer. my application relies heavily on incremental static regeneration. excuse me while I scramble to find another hosting option...
dmsolutionz commentedon Mar 26, 2021
Have you attempted this solution yet?
evangow commentedon Mar 26, 2021
Are you asking me? If so, no. My application doesn't rely on ISR. I would use it if this repo supported it, but it's not strictly necessary for my use-case.
adamdotdevin commentedon Apr 2, 2021
I'm looking into implementing this, if nobody else has started the work. I leverage
lambda-at-edge
in my CLI tool, Ness (https://github.com/nessjs/ness) and want to give users of Ness ISR support.dphang commentedon Apr 3, 2021
@adamelmore sounds good, thanks! Sorry I hadn't a chance to work on this yet. Do let us know if you need any guidance - I think for this feature this would require adding some new SQS / regional Lambda to trigger the incremental static regeneration / revalidate behavior, which can be called from Lambda@Edge using SQS client.
dmsolutionz commentedon Apr 8, 2021
Where on the roadmap is this placed at this point? It is genuinely one of the best features I've seen from Next.
adamdotdevin commentedon Apr 10, 2021
Well, I don't know if there's a roadmap, per se; but, on my personal Trello board it's currently in the "Next Week" column 😅
I want this for my own side project, so I will be working on it; it's just a matter of when. I hope to have started in the next week or so, and then go from there.
adamdotdevin commentedon Apr 13, 2021
I'm interested in paying a bounty to anyone that wants to take this work on. I've got a lot on my plate but really want to see this completed.
Offering $1200 USD if anyone that stumbles on this wants to take this on. Message me here or on Twitter for details.
https://twitter.com/aeduhm/status/1382093398077796357?s=20
adamdotdevin commentedon Apr 18, 2021
Upping this to $2000 USD.
kirkness commentedon Apr 28, 2021
Keen on taking you up on that offer @adamelmore 😁
Just put in a WIP PR here - keen for everyone's feedback.
dphang commentedon May 22, 2021
I think this can be closed save for any bugs which I will track here: #1098. Thanks all!