-
-
Notifications
You must be signed in to change notification settings - Fork 455
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 #1028
Conversation
retryStrategy: await buildS3RetryStrategy() | ||
}); | ||
|
||
const { req, res } = lambdaAtEdgeCompat( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks to be just regular Lambda, so this should not be needed, right? You can just create a Node req, res in that case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct! Good catch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On second thought, the reason this is here is due to the fact that the original CloudFront event is actually passed through the queue to this lambda (the cloudFrontEventRequest
variable), and therefore although this is a standard lambda we are still serving the original CloudFront event. The ideal change would be to standardizee the request object throughout, which would be nice perhaps using some of the principles in the serverless-http package. However, might slightly sit outside the scope of this PR. Keen to know your thoughts @dphang!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, makes sense for now. I think it would be good to have a generic event since in the future this can be used for regular Lambda traffic (and maybe other providers)
I didn't see any handler for rate limit errors on SQS queue in your code. Thought about ISR quite a bit before. See my comment here: #804 (comment) After I posted the comment above, I spent a while thinking about the edge cases I listed and the SQS queue. The downside of using SQS to deduplicate requests is that you have to use a FIFO queue, which only supports 300 messages per second (300 send, receive, or delete operations per second) without batching. The example I had in my head when thinking about this issue was Reddit. They could benefit a great deal from ISR on /r/popular (as an example), because the content can be stale for 60 seconds and it wouldn't really matter. The second that the page expires though, there would be 10,000s (100Ks? 1Ms?) of requests to regenerate /r/popular. The SQS queue would throw a rate limit error on any of those requests past the first 300 and all of the other pages requesting revalidation would also run into a rate limit error. To handle the rate limit, you could have all initial requests go to a non-FIFO SQS queue, then have that queue send the messages to the FIFO queue. You would also be able to batch the requests from initial queue and send them over to the FIFO SQS queue. By batching the requests, you can process 3,000 requests / second (300 message limit * 10 messages / batch). If the FIFO queue returns a rate limit error, then you can just leave those in the initial queue and retry later. In other words: Ultimately, I think adding a FIFO queue makes things too complicated for deduplicating requests due to the rate limit. I would just regenerate the page via an async lambda, and if it gets regenerate a whole bunch of times because a page is popular, you're just spending some additional money to handle those requests. Those pages are probably currently using SSR with right now anyway where every request already currently regenerates the page, so ISR is already going to be a money saver. If you just use an async lambda, you'll save a little bit less money but you can avoid the complexity that SQS would involve to do it The Right Way. I could probably be convinced either way is good, but I think just 1 FIFO queue might be an issue. Unrelated: |
Hey @evangow, cheers for the message. I've not investigated this in-depth yet, however, have implemented the queue with fifo + message deduplication so that the receiving lambda will only be invoked at most once for every time a page needs to be regenerated. What I don't know is whether the queue's rate limit is depleted even when the message is discarded when its a duplicate? |
I didn't see anything in the docs about that. If it's the case that only the 1st instance counts against the API usage limits, then that would be awesome. I assumed the docs to mean that any sqs.send() call would count against your usage even if the message is deduplicated. |
Nice 👍 I also started working on this yesterday, but your efforts are clearly much further along. However, I don't think your cache-control logic in default-handler will work. It isn't sufficient to just check the prerender manifest for revalidate times, since 1) dynamic pages won't be there and 2) revalidate is returned from Here's my series up to the point where actual revalidation logic would be needed: master...jvarho:isr-wip |
Thanks, @jvarho! I hadn't considered these cases and I think your approach of using the |
The way I used it, the Expires header is only used to store the time in S3. It is the Cache-Control I set here that actually matters for Cloudfront: master...jvarho:isr-wip#diff-f4709477772e5badfaaf543f6104de4b4ab3bf8300a056c088f84c184d800288R828 The Expires header could – and probably should – be removed at that point. (The reason to use Cache-Control is of course the distinction between Cloudfront and browser caching.) |
Ok, so you set the Expires header when creating the file in S3, and then use that to define the smax header at which point in the Origin Response the Expires header can be removed? |
Exactly 👍 |
That looks correct for the regeneration path. However, you still need something like my changes in jvarho@bd988a4 and jvarho@4423a47 to get prerendered and fallback-rendered pages to expire, do you not? (I think you could just cherry-pick those two commits.) |
You are probably right, I've not looked at those behaviours yet, but if I can just cherry-pick then that's awesome! |
Update: 8a0d5ff Frustratingly there is no simple way of setting the |
packages/libs/lambda-at-edge/src/lib/getStaticRegenerationResponse.ts
Outdated
Show resolved
Hide resolved
packages/libs/lambda-at-edge/src/lib/triggerStaticRegeneration.ts
Outdated
Show resolved
Hide resolved
Sweet, thanks for checking, odd that it worked locally! I'll add this today along with making sure these tests aren't too flakey! |
That permission appears to already exist on the default lambda's policy: serverless-next.js/packages/serverless-components/nextjs-component/src/component.ts Lines 478 to 482 in 1f757d1
Perhaps the policy isn't getting updated? |
Yup, thanks! I think the policy might not be getting updated after it's already deployed...but I think it should be ok to ask users to update it |
@dphang - the e2e's are killing me slowly 😂.... It looks like the issue is caused by the shared libs not reliably being built due to yarn failing to install (which I understand is a known issue). I've gone through each of the tests locally and each app/test suite passes. I'd be fairly keen to work on this issue in a follow-up PR. My suspicion is that the issue is related to the |
Yep hopefully it helps. I do see that the code limit is reached (75 GB), I'll add a script to clean up old Lambdas which can run in the finalize step. Hopefully we can get it checked in today, just wanting to make sure that the e2e tests are relatively stable at this point. |
Looking at this I learnt about |
"lambda:CreateEventSourceMapping", | ||
"iam:UpdateAssumeRolePolicy", | ||
"iam:DeleteRolePolicy", | ||
"sqs:*" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For better security, we should update this to just the sqs permissions needed, but not blocking for this PR
I think so far it looks good to me, really great work on this! If you have any improvements like updating the README on how to use this feature (e.g all the SQS permissions etc needed), improving tests, cleaning up more of the code etc. please feel free to add in future PRs. |
Awesome work guys, |
Nice work, everyone! 👏 |
Adds Incremental Static Regeneration!
Related issues #804 #995
serverless
CDK
revalidate
property, as per @dvarho's commentThe implementation looks like:
Expires
header, or the original build page had a revalidate property)Expires
header is in the future and if so apply a cache-control header such that the object is stored in the edge until its ready for revalidation.You can check out a demo of this feature here, check the headers and the response times to try to get your head around its behaviour. The code for this page looks like:
👍