Stale While Revalidate (SWR)
Flightcontrol has an option to add stale-while-revalidate
cache-control header support to CloudFront because it does not support Stale While Revalidate (SWR) natively.
The stale-while-revalidate cache-control header
(opens in a new tab) is useful when you care more about the speed of all user’s requests more than you care about users seeing the latest data. When used, the Content Delivery Network (CDN) will serve stale content to the user while refetching the latest data in the background so that the next user will get the latest data.
When to use SWR?
You want to use Stale While Revalidate if you use any of the following:
- Next.js Incremental Static Regeneration (ISR)
- Manual use of the
stale-while-revalidate
cache-control header
Please note that using the CloudFront SWR feature will slightly increase your AWS usage & cost via lambda invocations. However, it will also decrease your server load, so the end cost should be roughly the same.
Next.js Incremental Static Revalidation (ISR)
Next.js ISR is enabled by adding the revalidate
prop to your getStaticProps
function. See the Next.js docs (opens in a new tab) for more details.
Stale While Revalidate Cache-Control Headers
Flightcontrol’s SWR feature will also benefit you if you are manually controlling the cache-control
headers in your web application.
Setup
Dashboard
-
When creating or editing an environment with a Fargate service, the “Cloudfront stale-while-revalidate” toggle will be located under the advanced toggle at the bottom of the service config menu. Click the toggle to enable this feature.
Flightcontrol.json
- You can also add this feature in your
flightcontrol.json
file by adding a field in the fargate services config like the following:"enableCloudfrontSwr": true
How SWR Works
In a broad sense, stale-while-revalidate allows you to cache a static page in a CDN and serve it to users while refetching an updated page. This allows you to add updates to your static site at runtime, while also creating faster load times for the user and decreasing server load. The time for the cache to become stale is controlled via cache-control headers, which can either be set in NextJS or manually.
- When you request any file from your website, for example: (
www.example.com/swr
). This is a path that usesstale-while-validate
caching policy. - Usually without the new CloudFront SWR feature, this is considered aMiss, and CloudFront will trigger the origin (server) asking for the content.
- Once the content is delivered by the server, CloudFront will cache, to the maximum age that the server identifies in the
Cache-Control
header.- Let’s assume this time is 5 seconds for the sake of the article
- During the 5 seconds, CloudFront will serve the content directly from its cache, without asking the origin server for updated content.
- After the 5 seconds, the content is considered expired, and immediately, CloudFront will consider it a Miss, and trigger the origin server again.
Let’s see how that is different with the new CloudFront SWR feature
- Assuming you are requesting the same
/swr
path. - As this is the first request ever, CloudFront checks its cache, and finds no matches, so it is considered a MISS, and instead of triggering the origin server immediately, it triggers our custom developed Lambda@Edge function.
- This Lambda@Edge function does not find this path
/swr
in its dictionary, so it adds it, and asks for offline async fetch for this content.- As this is the first request, in order not to fail the request, the lambda function, will instruct CloudFront to relay the request to the origin server, and serve the content.
- The offline async process, will retrieve the content one more time from the origin server, and caches a hash for the content, and its expiration time.
- Now for the next 5 seconds, CloudFront will be serving the content from its own cache.
- After the 5 seconds, CloudFront will trigger the Lambda@Edge function again, asking about the
/swr
path, this time the Lambda@Edge function is well prepared, and finds the path in its dictionary, and compares the hash of the content cached with CloudFront to the hash that we retrieved from the server, if match, Lambda@Edge instructs CloudFront to consider the content as valid, and serves the content from its cache (origin server is not triggered). This is considered a RefreshHit (i.e. the content is still the same, and CloudFront refreshes it from its own cache). - Once the content expires on the Lambda@Edge dictionary, it asks for a refresh offline async. And continue to serve the content cached with CloudFront.
What if the content actually changed?
- If the content changed on the origin server, the offline async process, will determine the new hash.
- This time, the Lambda@Edge function, determines that the hash for the cached content is different, so for the very first request it receives for this path, it asks CloudFront to refresh its content from the origin server.
- In the meanwhile till CloudFront refreshes its content, the Lambda@Edge will ask CloudFront to serve the cached content for any subsequent requests (so the origin server is hit only once by CloudFront)