Caching “Like” and “Share” Buttons
In a blog post about caching with tracking cookies, I explained how Fastly’s edge scripting language allows businesses to cache things that were previously uncacheable as well as send data back via our real-time logging system.
But what happens when you need to cache something more complicated, such as a product that handles user interaction?
Caching “Like” and “Share” Buttons
A good example is the social widget you see on virtually every content site:
The sharing widget will typically consist of a few small icons, some HTML and CSS, and other JavaScript. Typically, the icons are very small and rarely change, but they’re served billions of times a day.
These sharing icons are perfect candidates for very aggressive caching. When we chat with customers about this, we recommend setting a very long time to live (TTL) and an even longer grace period (also known as a “serve stale” period which will continue showing your content even if your origin server is unavailable). Then, if our customers do want to change anything, they can issue a purge and Fastly will fetch the new assets immediately.
Since the working set is so small, for similar setups we typically see a 100% cache hit ratio. This means customers can dramatically lower their hosting costs while ensuring that they’ll be able to weather request spikes that occur when, for example, a story that has the sharing widget embedded suddenly sees a lot of traffic, without breaking a sweat.
Caching the Share Count
There is a dynamic element to a sharing widget: the share count. There are typically two ways to deliver this:
Baked into the HTML or JavaScript that you deliver
Via an API that the JavaScript calls
Doing it the first way would ruin your cache hit ratio for all those previously cacheable assets, since you’ll need a new version of the widget every time a user “Likes” the page. By doing it the second way, you can minimize the problem to just one API call, which isn’t too bad.
With Fastly, there is a third option. Here’s how it works:
The share count actually doesn’t need to be absolutely up-to-the-second accurate. Instead, Fastly caches the results of the API call for ~5 minutes (this number is completely configurable depending on traffic and other factors). Every time a user clicks on the social share button, JavaScript first updates the counter on the page and then does an API call to update the counts. Fastly can either pass this API call through to origin or intercept it and return a 200 response, but emit a log message with the relevant information.
That way, a user can see that their “Share” has been counted, the customer caches as much as is humanly possible and keeps the load off their server, and everybody is happy.
#
# All of this can be done easily via the Fastly API but we present it here in VCL for brevity
# Simply create a new "Content" object and attach a request condition with the condition
# req.url ~ "^/like" && req.request == "POST"
# (or whatever your "like" API endpoint is).
#
# Then create a new Logging object and attach the same condition to it.
#
sub vcl_recv {
if (req.url ~ "^/like" && req.request == "POST") {
error 702 "OK";
}
}
sub vcl_error {
if (obj.status == 702) {
set obj.status = 200;
set obj.http.Content-Type = "text/plain; charset=utf-8";
synthetic {"OK"};
}
return (deliver);
}
sub vcl_log {
# The log line can be whatever you'd like it to be - here I just use the standard log line
if (req.url ~ "^/like" && req.method == "POST") {
log {"syslog " service.id " my_like_endpoint :: "} req.http.Fastly-Client-IP {" "} {""-""} {" "} {""-""} {" "} now {" "} req.request " " req.url {" "} resp.status;
}
}
However, if the user navigates away from the page and then comes back (or clicks through to the second page of a multi-page article) within that 5 minute window, they’re going to see that their “Share” hasn’t been counted, which is confusing.
In that case, there’s a fairly easy work around. When the user clicks “Like” or “Share,” the JavaScript updates the counter on the widget and sets a cookie with the new count in it before making the call back to register the “Like” or “Share.”
When the social share button is loaded again, the widget code first checks to see if the cookie is present (or alternatively, is newer than the last-modified of the API call) and uses those values. If not, it uses the values from the API.
We still avoid the additional call back to the customer’s origin server, but the user is happy that their “Like” or “Share” has has been counted.
Meanwhile, the widget will load incredibly quickly and the customer will have reduced their load back to their servers from potentially billions a day to:
1 request per asset every time they update their widget
1 request every 5 minutes to fetch the updated count
1 POST request every time a user hits the social share button (unless they use logs instead)