The goal of caching is to avoid having to generate the same response twice. The business reason to use caching is that it helps us gain speed, reduce server load, and reduce our dollars spent.
A RESTful API being cacheable is one of the most important REST constraints and one of the most important ways of handling large scale.
Tip 1: Use Expiration
A gateway cache (or a “reverse proxy cache”) reads the headers “Cache-Control” or “Expires” and then decides how long to cache them for. Once that timeline is hit, the cache then calls them stale and waits for another request to get them.
Essentially if Howard requests a page, and it’s not in the cache than the cache sends the request to the backend which then responds back to the cache with the page. The cache then stores the request for whatever the Expiration time is.
If Bob requests the same page or same resource it then goes to the cache and sends it only from there if it’s not yet stale. We aren’t saving any bandwidth here but we are saving CPU usage, database round trips, and potentially other costs.
This is a common technique and arguably one of the easiest to implement for the web. If you are using an API Gateway (Introduction to API Gateway ) it may not require any code changes.
Tip 2: Use the Cache-Control Header
The Cache-Control HTTP header holds instructions (“directives”) in requests and responses for how to handle caching in shared caches like proxies or content delivery networks. Caching directs are case-insensitive but I always recommend using lowercase.
no-cache/no-store or private should be used to avoid caching. I typically recommend providing this header for GET requests as public or max-age.
|public||Indicates that resource is cacheable by any component.|
|private||Indicates that resource is cacheable only by the client and the server, no intermediary can cache the resource.|
|no-cache/no-store||Indicates that a resource is not cacheable.|
|max-age||Indicates the caching is valid up to max-age in seconds. After this, client has to make another request.|
|must-revalidate||Indication to server to revalidate resource if max-age has passed.|
Tip 3: Cache Validation
When a resource is expired it needs to be validated or fetched again. It’s always wise to consider returning an ETag and Last-Modified header in HTTP REsponses.
The ETag HTTP response header is an id for the specific resource version. A lot of the time people will use a checksum or hash of the resource so that it’s different each time it changes.
The ETag value can then be used in the “If-None-Match” Reqiest header. If it’s not changed, the server should respond with a 304 Not Modified HTTP status code. The client should then used the cached resource.
The Last-Modified HTTP response header contains the date and time that it was last modified. It should be used in a way similar to the ETag header, but it’s less accurate. If you use this header, make sure to provide the timezone back.
Tip 4: Cache Aside (Lazy Loading)
Cache aside keeps the cache updated through the application. The application checks to see if data exists in the cache. If it exists in the cache it reads from the cache, if it doesn’t it reads from the data source and then writes to the cache. The result is then returned.
Cache aside is the easiest cache to implement but the most difficult to manage. Cache invalidation can be difficult because you won’t necessarily check if it’s been expired.
Redis and memcached are popular applications for caching.
Tip 5: Look at Model Caching
Not really, an HTTP Caching technique but don’t be scared to go further and do model or database level caching for frequent queries that don’t have changing data.
The blog article 6 Tips For Better Handling Traffic Spikes covers quite a few different techniques for caching and removing server load. Introduction to Relational Database Scaling also covers database caching and a few other techniques for handling load too.
Also published on Medium.