Fastly and Rails

I gave a presentation called “CDNS on Rails” at the January Bmore on Rails meetup. These are my notes.

CDNs on Rails

What is a CDN and why would I want one?

A CDN is a Content Delivery Network (or Content Distribution Network). Generally, this means that a set of geographically-dispersed servers allow you to serve your content from locations that are way closer to your end user. CDNs generally promise fast read times and high uptimes.

You want one so your end-users get their files faster. And also they provide a higher uptime than your source server. A CDN can also protect the source backend server from multiple similar requests, keeping it from being swamped by an influx of traffic.

What are some examples of CDNs?

  • CloudFlare CDN
  • MaxCDN
  • CoralCDN (free)
  • Akamai
  • Amazon CloudFront
  • Microsoft Azure
  • Fastly

How does a CDN work?

You upload your content to the CDN, which distributes it (propagation) to all the nodes around the world. You direct your users to download the resources from the CDN nodes rather than from your source node.

Which CDN node is used is determined based on the end user’s location and network.

A “pull-through” CDN allows you to seamlessly distribute new versions of content to the CDN. The CDN nodes are “in front of” your server, and they are instructed to cache the content as each request flows through them.

Why Did OrderUp Choose Fastly?

  • instant purge
  • pull-through caching
  • fastly-rails and fastly-ruby gems
  • SSL on both sides
  • control panel
  • it’s based on varnish
  • all-SSD network

Integrating with Fastly

  • Use a CNAME or ALIAS DNS record to point your domain name to Fastly’s servers
  • Configure Fastly to use your server as the “backend”. SSL is supported.
  • Test. Everything should work as always, but with traffic flowing through the Fastly CDN.
  • Choose which assets (pages, images, scripts) you would like to cache. Set HTTP headers on them:
    • Cache-Control: public, no-cache
    • Surrogate-Control: max-age: 86400
  • Test. Those assets should be served from CDN for one day. Once they expire, the next request will continue down to the backend server, and the new version will be “pulled-through” and cached for another day.

Fastly-Rails Set up

Set up with a Rails app couldn’t be much easier:

  • Install the fastly-rails gem
  • On requests you wish to cache, call set_cache_control_headers in the controller action

Invalidating the cache

Also called a “purge”, you do this when you know that content has changed and you want the next request to cause the CDN to get a fresh copy. For example, if you are caching a restaurant menu page on the CDN, you’ll want to invalidate that html page when a new item is added to the menu by an admin.

Fastly has a “surogate key” system in place, it’e like a giant hash table. You tag every piece of content with one or more keys, and then you can purge by key. So if you have a restaurant menu page and a restaurant marketing page, add the same key to each.

Surrogate-Key: restaurant/123

Using the Fastly API (via fastly-rails or fastly-ruby) you can issue a purge of key ‘restaurant/123’, which will remove both the menu page and the marketing page from the CDN. Upon the next request, new pages will be requested from the backend server and stored on the CDN.

Results at OrderUp

Right now, about 30% of our web and api requests are served by the CDN. This has shielded our Heroku backend a bit, allowing us to not need to raise our dyno count in heavy-load times. By making even more pages cache-able on the CDN, we can reduce our cpu load on Heroku, allow us to serve even more traffic at scale.

Gotchas

  • You don’t want to cache dynamic content, so be sure the html you generate is not user-specific. You’ll need to use JavaScript to enhance the page in the browser.
  • Fastly will not cache any response that has a Set-Cookie header.
  • There could be multiple edge servers in the CDN’s network. So don’t expect only one hit on your Rails app for each page, you might get many. (unless you configure Fastly Origin Shielding)
  • You’ll want to set up your surrogate keys in a way that supports your invalidation needs. In our case we make frequent edits to individual restaurants, so each page that has restaurant data on it includes the key for that restaurant. We also sometimes want to purge all the html views, so we have a special key that we can use to purge all restaurant.html pages (Surrogate-Key: html/restaurants/show).

Slides: