Skip to content
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

Support for incremental SSG (ISG/ISR) #151

Closed
joostmeijles opened this issue Aug 31, 2020 · 44 comments · Fixed by #986
Closed

Support for incremental SSG (ISG/ISR) #151

joostmeijles opened this issue Aug 31, 2020 · 44 comments · Fixed by #986
Labels
priority: high type: feature code contributing to the implementation of a feature and/or user facing functionality

Comments

@joostmeijles
Copy link

It seems that next-on-netlify current does not support incremental SSG (https://nextjs.org/docs/basic-features/data-fetching#incremental-static-regeneration).

Are there any plans to support this?

@cassidoo
Copy link

This package actually does support it, to an extent!

Here's a demo:
https://sonnet-18-incremental.netlify.app/

You can use these URLs in the box there for the demo:
https://github.com/cassidoo/next-netlify-starter
https://github.com/netlify/next-on-netlify

Here's the code for it:
https://github.com/cassidoo/sonnet-18/blob/static-props/pages/compare/%5B...slug%5D.js

Do you have any other questions related to this?

@joostmeijles
Copy link
Author

joostmeijles commented Sep 2, 2020

Can you please elaborate to what extent there is support?

Is there for example only support for the unstable_revalidate (https://github.com/cassidoo/sonnet-18/blob/f3f21bcb3b27bbfbdcac73c8798eb4c951ded06a/pages/compare/%5B...slug%5D.js#L48)?

In my test revalidate: 1 did not generate a serverless function.

@joostmeijles
Copy link
Author

joostmeijles commented Sep 9, 2020

I created netlify/next-on-netlify#37 to explore the option of adding incremental static regeneration.

FinnWoelm referenced this issue in netlify/next-on-netlify Sep 16, 2020
Ideally, pages with incremental static regeneration (ISR) should be
statically pre-rendered and then re-rendered upon request at the
provided revalidation interval. This is currently not possible, because
we can only either have a static page or have a page that is server-side
rendered.

As a temporary workaround, we will be SSRing pages with ISR. This means
they will be less performant that regular static pages, but at least
their content will be fresh. We believe that a focus on the freshness of
the content matches the intention of the user more closely than a focus
on the page's performance.

Discussion: https://github.com/netlify/next-on-netlify/issues/35

Read more about ISR:
https://nextjs.org/blog/next-9-5#stable-incremental-static-regeneration
@FinnWoelm
Copy link

Hey @joostmeijles,

You are right — there is currently no support for incremental static regeneration (ISR).

Ideally, pages with ISR should be statically pre-rendered and then re-rendered upon request at the provided revalidation interval. This is currently not possible, because we can only either have a static page or have a page that is server-side rendered. Netlify is investigating and researching possibilities for fully supporting ISR.

In the meantime, your proposal to server-side render pages with ISR is a fantastic workaround. This means that these pages will be less performant than regular static pages, but at least their content will be fresh. I believe that a focus on the freshness of the content does indeed match the intention of the user more closely than a focus on the page's performance.

Based on your excellent work done in netlify/next-on-netlify#37, I created a WIP branch to server-side render pages with incremental static regeneration: incremental-static-regeneration. It looks like it's working well — but I would like to refactor some code before I merge it into master. So it may be another week before it ships.

Again, thank you for raising this issue and for your very helpful proposal! 😊
- Finn

@cassidoo
Copy link

Thanks so much for this work @joostmeijles and @FinnWoelm! This is so exciting to get even more support for ISR. Are there any other gaps that need to be filled to get this better supported?

@joostmeijles
Copy link
Author

@cassidoo I think Netlify stale-while-revalidate support is needed to fully support ISR.

lindsaylevine referenced this issue in netlify/next-on-netlify Oct 1, 2020
- Copy host property from headers to multiValueHeaders [#44](#44)
- More support for ISR (getStaticProps with revalidate) [Discussion](https://github.com/netlify/next-on-netlify/issues/35) / [Commit](ef45cc5)
- Fixed redirect for index with getServerSideProps [#39](#39)
@joostmeijles
Copy link
Author

It seems that ISR somehow stopped working on the Netlify CDN.

The serverless (SSR) functions complete but do not return and therefore result in a timeout after 10 seconds.

For example:

{"errorMessage":"2020-10-21T14:05:48.488Z 604afa24-2c41-46d8-840e-a925c1e03587 Task timed out after 10.01 seconds"}

@FinnWoelm @cassidoo any clues ?

@FinnWoelm
Copy link

@joostmeijles things are still working on my end! The last release is 20 days old, so there shouldn't have been any change sincethen.

Is it possible that you use some sort of third party library that keeps a connection open? Last week, @garrypolley reported something like this when using Prisma: netlify/next-on-netlify#55 (comment)

Otherwise, happy to have a look at your code and/or see if we can reproduce this issue somewhere.

@joostmeijles
Copy link
Author

joostmeijles commented Oct 22, 2020

Is it possible that you use some sort of third party library that keeps a connection open?

Possibly! Will dig a bit deeper 🙂

@FinnWoelm
Copy link

FinnWoelm commented Oct 22, 2020

Okay, sounds good! Quick test would be to add a super simple page without any imports and seeing if that works.

Something along the lines of this:

// pages/mypage.js

export default function MyPage() {
  return (
    <p>Hello World</p>
  );
}

export function getStaticProps() {
  return {
    props: {},
    revalidate: 1
  };
}

@davetayls
Copy link

I am trying to get this working for a project and am finding that the pages are always hitting the function and are not being cached at the CDN. Is that the behaviour you are finding?

@joostmeijles
Copy link
Author

joostmeijles commented Dec 3, 2020 via email

@lindsaylevine lindsaylevine pinned this issue Dec 20, 2020
@iDVB
Copy link

iDVB commented Jan 21, 2021

@joostmeijles If I'm understanding this correctly, ISR is now actually just SSR under the hood when hosted on Netlify.
I think it begs the question then, why would I choose to use ISR?
Would it not just be simpler to use SSR?
I would love to know the technical details here, what it means for end-user performance and how Netlify is working under the hood.

@joostmeijles
Copy link
Author

@joostmeijles If I'm understanding this correctly, ISR is now actually just SSR under the hood when hosted on Netlify.
I think it begs the question then, why would I choose to use ISR?
Would it not just be simpler to use SSR?
I would love to know the technical details here, what it means for end-user performance and how Netlify is working under the hood.

As far as I know Netlify does currently not support stale-while-revalidate. Without this feature it is not possible to implement ISR (and thus SSR is used). SSR basically is a blocking call, and the end user waits for the result to be calculated, while true ISR calculates the result in the background and returns the previous result to the user.

@iDVB
Copy link

iDVB commented Jan 21, 2021

Thanks @joostmeijles! That makes sense.
Current Netlify-ISR state:
So it sounds like Netlify-ISR is also then still a blocking call?
Still curious why then would you want to support both if you could alternatively just forward developers on to using SSR?

Intended/Normal ISR State
A co-worker and I are really trying to wrap our head around the intended mechanics of ISR and how its supposed to actually work? (Say, assuming someone deploys to Vercel)
How does the browser, the cache, and the function work together to update the users stale page? Is it long-polling or something? Or is it only each subsequent request that gets the fresh update?

@lindsaylevine
Copy link
Contributor

lindsaylevine commented Jan 25, 2021

@iDVB hey! what @joostmeijles said is correct - netlify does not currently support SWR (stale-while-revalidate). so yes, currently, ISR on netlify is a blocking call.

Still curious why then would you want to support both if you could alternatively just forward developers on to using SSR?

the current behavior is temporary. we want to and are aiming to support ISG/ISR in the way that @joostmeijles describes ("true ISR calculates the result in the background and returns the previous result to the user") - or as close to this behavior as possible without compromising our caching and jamstack philosophies.

re: wrapping your heads around the intended mechanics, i highly recommend watching a few minutes of ryan florence's explanation in this video (timestamped!): https://youtu.be/bfLFHp7Sbkg?t=907

to answer your question though, yes, each subsequent request gets the fresh update

hope that helps!!! :)

@lindsaylevine lindsaylevine unpinned this issue Mar 5, 2021
@lindsaylevine lindsaylevine transferred this issue from netlify/next-on-netlify Mar 16, 2021
@lindsaylevine lindsaylevine pinned this issue Mar 19, 2021
@lindsaylevine lindsaylevine added type: feature code contributing to the implementation of a feature and/or user facing functionality priority: high labels Mar 19, 2021
@dlaugharnTAG
Copy link

Howdy, I had a question about three lines that log out when building:

🔥 Copying pre-rendered pages with getStaticProps and JSON data to out
💫 Setting up pages with getStaticProps and fallback: true as Netlify Functions in netlify/functions
💫 Setting up pages with getStaticProps and revalidation interval as Netlify Functions in netlify/functions

We have ~25 pages that are pulling from markdown using fs, and they have neither fallback nor revalidation values set, however they all end up logged under Functions bundling, which ends up taking anywhere from 4-7 minutes. Is this expected behavior? The line about copying pre-rendered pages to out would lead me to believe it isn't.

If you can look at build logs, deployment 6058d81084773b0007928857 would be an example.

@lindsaylevine
Copy link
Contributor

@dlaugharnTAG howdy! fair question! i believe, per https://github.com/netlify/netlify-plugin-nextjs/blob/main/src/lib/steps/setupPages.js and here, that we log these lines no matter what. if there are no file names underneath those particular logs you quoted (the file names get logged in the pages loop in setup.js), then there aren't actually functions set up for these files (or there shouldn't be). the line about copying pre-rendered pages to out (aka your publish dir) is specifically talking about copying prerendred html to your publish dir. unfortunately, for preview mode, we also have to generate a function for every getStaticProps page. we may consider adding some sort of configurable option to disable preview mode; this wouldn't be the first time someone's given feedback about the additional bloat of our preview mode support.

also: i think i need the whole link to the deploy logs (including the site name) to see them 🤔

@mikaelgson
Copy link

mikaelgson commented Apr 6, 2021

the current behavior is temporary. we want to and are aiming to support ISG/ISR in the way that @joostmeijles describes ("true ISR calculates the result in the background and returns the previous result to the user") - or as close to this behavior as possible without compromising our caching and jamstack philosophies.

@lindsaylevine Do you have an estimate on when this will become a reality? This would make Netlify a viable alternative to Vercel for sites depending on ISR.

@lindsaylevine
Copy link
Contributor

@mikaelgson stay tuned 👀 what i can say right now is that we are a lot closer to offering "our version of ISR" than ever before! i will of course be sure to update this thread when i can share more info. :)

@raaims
Copy link

raaims commented Apr 8, 2021

Hi @lindsaylevine,

We're having the exact same problem. We haven't implemented previews yet but our getStaticProps pages are being bundled into functions. Let me know if you need additional information (and where should I send you the info).

Thanks in advance 😄

@tbgse
Copy link

tbgse commented Apr 29, 2021

@lindsaylevine first of all thank you for all the amazing work on this plugin 🙏

We've been exploring Netlify as an alternative to Vercel for our Next project for a while now, and with On-demand Builders this now seems a lot closer to reality. I'm currently trying to plan the internal timelines for us to test next on Netlify with ODB and was wondering how "stable" the experimental branch / ODB is considered at the moment - is this usable for a production environment yet, or should we wait a bit longer? Also curious how close you might be to an implementation of the revalidate property 👼

@lindsaylevine
Copy link
Contributor

@tbgse thanks for the kind words 😊 i would say the odb branch is reliably stable. it's been in a separate tagged release for 2-ish weeks now for testing and we haven't had any issues reported. that means, within the next week or two, we'll likely merge it into main and release it as the standard/default plugin!

i pinged internally earlier to see how much i could share re: revalidate - there is something we have in the works that would address the same problem revalidate is specifically trying to solve but can't really say when that's coming or what it is quite yet. as always, hang tight~!!!

@Nicoowr
Copy link

Nicoowr commented May 9, 2021

Hi all,
As a workaround I think it's possible to achieve incremental SSG by combining this plugin (setting fallback: true or blocking) with useSwr.

@lindsaylevine lindsaylevine changed the title Support for incremental SSG Support for incremental SSG aka Incremental Static Regeneration (ISG/ISR) May 18, 2021
@lindsaylevine
Copy link
Contributor

updated title to help better direct incoming users looking for ISR stuff :)

@lukeocodes lukeocodes changed the title Support for incremental SSG aka Incremental Static Regeneration (ISG/ISR) Support for incremental SSG (ISG/ISR) May 18, 2021
@saidattax
Copy link

Is there way I can trigger the on-demand builder to "rebuild" a particular page?

@mraerino
Copy link
Member

mraerino commented Jun 9, 2021

@saidattax providing this mechanism is something we are very careful with because it is pretty easy to get your site in an inconsistent state with such a capability. since users have no tools to debug this, it would increase the load on our support team to debug sites, which we want to avoid.

I can generally recommend these strategies right now:

  • trigger a new deploy using a Build Hook if your content changes daily
  • not cache your SSR response at all if your content changes hourly or more often

nevertheless we are very interested in hearing about your usecase and how we can solve it better in the future.
would you mind sharing with us what you're trying to achieve and why the options i lined out might not work well for you?

@saidattax
Copy link

saidattax commented Jun 10, 2021

@mraerino Thanks for reply

The content for my sites is updated on-demand from a dashboard. I could indeed trigger a new deploy, but I need the build to be quick. Builds take 30-60 seconds usually.

Currently, the data for the site is fetched from a database and SSRed. However, in the case of data-server downtime, I'd need a cached version to be sent.

Hence, I wanted to trigger ODBs to build specific pages that need updating.

Thanks

@tbgse
Copy link

tbgse commented Jun 23, 2021

@mraerino we are also waiting for a revalidate feature where we can regenerate pages in the background. We use this for example to allow our marketing team to update localization files, inject experiment data, adjust content in a CMS for a single page etc. those changes will then be picked up during revalidation through getStaticProps and updated without requiring a full redeploy.

Since we have a lot of individual pages (20k+) running a full redeploy is very resource and time consuming, so through revalidation that gives us a bit more flexibility where pages can update one by one as needed.

@mraerino
Copy link
Member

mraerino commented Jun 28, 2021

how would you handle data changes that affect more than one page?
e.g. you change the title of a post and a bunch of your category overviews or taxonomy pages need to change?

@tbgse
Copy link

tbgse commented Jun 28, 2021

Since we're running an ecommerce site that is usually not a problem. E.g. we have a ton of marketing related pages, but no real "overview" page where all the titles would be displayed, so there are rarely situations where a change to one page would also impact other places.

The same goes for product pages, e.g. if the description of a single product is updated in our catalog, only that one specific product page would need to change. In other places where this content is used, we would mostly use client side rendering (e.g. in the cart / checkout etc.) so no re-generation would be needed besides that single page re-fetching some of it's data and updating on the edge.

There is of course always a tradeoff that in some situations a user might see stale content or inconsistent content between views - in most cases we deal with that by re-fetching data on the client side and apply modifications as needed. But for us this has mostly been a tradeoff that we've been willing to make if in return it allows us to give a bit more "real time" feel to our stakeholders in marketing / product who make edits to our content, while still having the benefits of SSG.

If you're up for it I'd be happy to connect and walk you through our setup a bit more in detail.

@mraerino
Copy link
Member

so if one of the stakeholders comes to you saying that some content is outdated, because it was not properly re-validated, how would you debug that situation?

would you just re-deploy the site and wait for the next time this happens?
do you have means to detect this situation inside your stack?
what kind of insights into cached pages would be useful to have on the netlify side?

@tbgse
Copy link

tbgse commented Jun 28, 2021

Right now we're logging into Sentry during revalidation, so if an error occurs during revalidation, we have at least "something" to look at. These errors are very rare for us, and like you said, running a full deploy usually then fixes the problem. Having more insight to debug this would be absolutely fantastic though.

I guess I could imagine an overview that shows which pages are scheduled for revalidation, when the page was last revalidated and a status marker if the last revalidation was successful or not. Access to a log of the builder would be really helpful too, if it tried to revalidate and ran into errors (e.g. maybe because an API was down while trying to revalidate the content). It would also be quite important that the stale content will continue to be served if the builder runs into an error. 95% of times if a revalidation failed in our case it was due to an API being not reachable or an odd response. In that case having the builder retry a couple of times (or the ability to manually click a button for it to try again) would be really nice.

It would be even better if this revalidation could not just be triggered by setting an interval (like it is implemented with next at the moment on vercel) but potentially could also be called directly / through a webhook so that for example CMS updates could immediately trigger a page to be revalidated.

@selrond
Copy link

selrond commented Jul 29, 2021

Came here after quite a while trying to understand why Netlify doesn't pick up forms.

This should definitely be mentioned somewhere inside the Netlify Form docs, I imagine it's quite common use case.

@jchan-ne
Copy link

Is there a timeline as to when this will be completed?

@ptim
Copy link

ptim commented Sep 22, 2021

what kind of insights into cached pages would be useful to have on the netlify side?

@mraerino response headers indicating the the page render mode and settings would be very helpful - eg: whether SSR or SSG, and if SSG, then fallback and revalidate settings. I understand that this is a NextJS responsibility (I think it probably requires running a custom server at present [clue]), but I'd find this helpful!

It would be even better if this revalidation could not just be triggered by setting an interval (like it is implemented with next at the moment on vercel) but potentially could also be called directly / through a webhook so that for example CMS updates could immediately trigger a page to be revalidated.

Agreed! Watch the pending NextJS feature: programmatically purging routes

There's been a lot of discussion here about programmatically purging routes that have been generated with ISR. We're happy to share we're actively working on this now 😄 We've spent the time since this feature launched evaluating options and have come up with a solution we think you'll love.
leerob on 23 Apr

@thucne
Copy link

thucne commented Oct 2, 2021

Hi there, does Netlify support ISR for NextJS app now? It seems that Vercel can support well ISR with 'revalidate' in getStaticProps, and I did use Next Essential plugin in Netlify and still, my static sites are not updated after 'revalidate' interval.

@nikhil-toobler
Copy link

The issue not persist in vercel, Netlify support team please share your updates, when the netlify users can expect full support of ISR.

@lindsaylevine
Copy link
Contributor

hey all. i know it's been a LONG time. please hang tight just a tad bit longer. we are about to release a huge plugin rewrite in the coming month (beta to come this week), and the ISR conversation continues to evolve for us internally. for now we still recommend DPR / using fallback: true without the revalidate flag.

@joonassandell
Copy link

joonassandell commented Oct 13, 2021

Hi, here is how I have solved this issue for now until this feature is implemented:

  • Removed all the revalidate options (or have set it to false)
  • Using react-querys initialData with props set from the getStaticProps. This query fetches the updated data from the CMS.
  • CI setup with a cron to create a new build with desired interval, which then updates the initial data.

Example:

const Page: NextPage = ({ initialData }) => {
  const { data } = useQuery('page', 
    () => fetchData(),
    {
      initialData,
    },
  );

  return <div>{data.value}</div>
};

export const getStaticProps: GetStaticProps = async () => {
  const data = await fetchData();

  return {
    props: {
      initialData: data,
    },
  };
};

export default Page;

This could create flashes of the old data in the UI if the updated and initial data doesn't match, however once the build is done again, this won't happen. This approach works for our use case because we have fairly small amount of pages to generate. As a result our homepage is loaded significantly faster because it's no more SSR.

@ascorbic ascorbic unpinned this issue Nov 5, 2021
@tbgse
Copy link

tbgse commented Dec 7, 2021

Hey all just curious if there has been any more updates to this topic? It's been a while since the last update. Is it still under consideration to be supported one day?

@ascorbic
Copy link
Member

ascorbic commented Dec 7, 2021

@tbgse we have a new release candidate of the build plugin that it worth trying

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority: high type: feature code contributing to the implementation of a feature and/or user facing functionality
Projects
None yet
Development

Successfully merging a pull request may close this issue.