Deep-dive on the Next Gen Platform. Join the Webinar!

Skip Navigation
Show nav
Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up
Hide categories

Categories

  • Heroku Architecture
    • Compute (Dynos)
      • Dyno Management
      • Dyno Concepts
      • Dyno Behavior
      • Dyno Reference
      • Dyno Troubleshooting
    • Stacks (operating system images)
    • Networking & DNS
    • Platform Policies
    • Platform Principles
  • Developer Tools
    • Command Line
    • Heroku VS Code Extension
  • Deployment
    • Deploying with Git
    • Deploying with Docker
    • Deployment Integrations
  • Continuous Delivery & Integration (Heroku Flow)
    • Continuous Integration
  • Language Support
    • Node.js
      • Working with Node.js
      • Troubleshooting Node.js Apps
      • Node.js Behavior in Heroku
    • Ruby
      • Rails Support
      • Working with Bundler
      • Working with Ruby
      • Ruby Behavior in Heroku
      • Troubleshooting Ruby Apps
    • Python
      • Working with Python
      • Background Jobs in Python
      • Python Behavior in Heroku
      • Working with Django
    • Java
      • Java Behavior in Heroku
      • Working with Java
      • Working with Maven
      • Working with Spring Boot
      • Troubleshooting Java Apps
    • PHP
      • PHP Behavior in Heroku
      • Working with PHP
    • Go
      • Go Dependency Management
    • Scala
    • Clojure
    • .NET
      • Working with .NET
  • Databases & Data Management
    • Heroku Postgres
      • Postgres Basics
      • Postgres Getting Started
      • Postgres Performance
      • Postgres Data Transfer & Preservation
      • Postgres Availability
      • Postgres Special Topics
      • Migrating to Heroku Postgres
    • Heroku Key-Value Store
    • Apache Kafka on Heroku
    • Other Data Stores
  • AI
    • Working with AI
  • Monitoring & Metrics
    • Logging
  • App Performance
  • Add-ons
    • All Add-ons
  • Collaboration
  • Security
    • App Security
    • Identities & Authentication
      • Single Sign-on (SSO)
    • Private Spaces
      • Infrastructure Networking
    • Compliance
  • Heroku Enterprise
    • Enterprise Accounts
    • Enterprise Teams
    • Heroku Connect (Salesforce sync)
      • Heroku Connect Administration
      • Heroku Connect Reference
      • Heroku Connect Troubleshooting
  • Patterns & Best Practices
  • Extending Heroku
    • Platform API
    • App Webhooks
    • Heroku Labs
    • Building Add-ons
      • Add-on Development Tasks
      • Add-on APIs
      • Add-on Guidelines & Requirements
    • Building CLI Plugins
    • Developing Buildpacks
    • Dev Center
  • Accounts & Billing
  • Troubleshooting & Support
  • Integrating with Salesforce
  • Language Support
  • Ruby
  • Rails Support
  • Building a Rails 5 Application with Memcache

This article was contributed by The MemCachier Add-on

MemCachier manages and scales clusters of memcache servers so you can focus on your app. Tell us how much memory you need and get started for free instantly. Add capacity later as you need it.

follow @MemCachier on Twitter

Building a Rails 5 Application with Memcache

English — 日本語に切り替える

Last updated April 10, 2024

Table of Contents

  • Prerequisites
  • Create your application
  • Deploy to Heroku
  • Install the MemCachier add-on and configure caching
  • Add some functionality
  • Add some caching
  • Expiring the cache
  • Built-in Rails caching
  • Further reading and resources

This article is archived. It is no longer receiving updates. It is presented here for historical reference only. We cannot guarantee that any statements made are still true or that the instructions will still work.

Adding caching to your web applications can drastically improve performance. The results of complex database queries, expensive calculations, or slow calls to external resources can be archived in a simple key-value store that can be accessed via fast O(1) lookups.

Static asset caching in Rails 3.1+ using Rack::Cache is outlined in this article

This tutorial will walk you through the steps of creating a simple Rails 5 application, deploying to Heroku, and using the MemCachier Add-on to cache expensive queries.

We’ve built a small demo Rails example app. Source code can be found here or Deploy to Heroku

Prerequisites

  • Basic Ruby/Rails knowledge
  • A locally installed version of Ruby 2.2+, Rubygems, Bundler, and Rails 5+. Note: this guide should also work for Rails 3 and 4.
  • Basic Git knowledge
  • A Heroku user account. Signup is free and instant.

Create your application

Use the rails command to generate your app skeleton:

$ rails new memcache-example
$ cd memcache-example/

First, specify the ruby version in you Gemfile:

ruby '2.5.0'

Next setup the database. In your Gemfile, change the line that reads:

gem 'sqlite3'

to

group :development do
  gem 'sqlite3'
end

group :production do
  gem 'pg', '~>0.21'
end

This ensures the app will make use of the Postgres database in production. Note: you may not need to add a version constraint to pg in the future but the current Rails version 5.1.4 is incompatible with the current pg version 1.0.0.

Now run

$ bundle install --without production

to update your Gemfile.lock file. The --without production option will prevent the pg gem from being installed locally.

Commit your changes (Note: if you use an older version of Rails you might need to create a Git repository first with git init):

$ git add .
$ git commit -m "Initial rails app."

Deploy to Heroku

Use the heroku command to provision a new Heroku app:

$ heroku create

then deploy to Heroku:

$ git push heroku master

Install the MemCachier add-on and configure caching

As noted in the MemCachier article, you need to install the add-on and the dalli gem. The optional memcachier gem is also recommended.

At the terminal, run:

$ heroku addons:create memcachier:dev

Modify your Gemfile to include dalli, a memcache client library, and memcachier, a simple gem that helps with setup:

gem 'dalli'

Next run

$ bundle install --without production

to install the added gems and update your Gemfile.lock file.

Now configure the default Rails caching to utilise the cache store provided by dalli by modifying config/environments/production.rb to include:

config.cache_store = :mem_cache_store,
                    (ENV["MEMCACHIER_SERVERS"] || "").split(","),
                    {:username => ENV["MEMCACHIER_USERNAME"],
                     :password => ENV["MEMCACHIER_PASSWORD"],
                     :failover => true,
                     :socket_timeout => 1.5,
                     :socket_failure_delay => 0.2,
                     :down_retry_delay => 60
                    }

To make it easier to see how this example works, temporarily turn off built-in caching:

config.action_controller.perform_caching = false

Add some functionality

Use the Rails scaffold generator to create an interface for storing and viewing a simple directory of names and email addresses:

$ rails g scaffold contact name:string email:string
$ rake db:migrate

Edit config/routes.rb to set contacts#index as the root route,

root :to => 'contacts#index'

Note: In Rails 3 apps you can now delete public/index.html.

Commit the changes, push to Heroku and use the following command to migrate your remote database:

$ git add .
$ git commit -m "Added first model."
$ git push heroku master
$ heroku run rake db:migrate

You should now be able to navigate to your app using heroku open to view your list of contacts. Follow the “New Contact” link and create a few records.

Add some caching

The code in your ContactsController looks something like this:

def index
    @contacts = Contact.all
end

Every time /contacts is requested, the index method will execute and a database query to fetch all of the records in the contacts table is run.

When the table is small and request volume is low this isn’t much of an issue, but as your database and user volume grow, queries like these can impact the performance of your app. Let’s cache the results of Contact.all so that a database query isn’t run every time this page is visited.

The Rails.cache.fetch method takes a key argument and a block. If the key is present, then the corresponding value is returned. If not, the block is executed and the value is stored with the given key, then returned.

In app/models/contact.rb, add the following method to the Contact class:

def self.all_cached
  Rails.cache.fetch('Contact.all') { all.to_a }
end

In app/controllers/contacts_controller.rb change

@contacts = Contact.all

to

@contacts = Contact.all_cached

Note that we cache all.to_a instead of all. This is because since Rails 4 Model.all is executed lazily and you need to convert Contact.all into an array with to_a in order to cache the actual contacts.

Let’s also display some statistics on the index page. Add the following line to the index method in app/controllers/contacts_controller.rb:

@stats = Rails.cache.stats.first.last

And add the following markup to the bottom of app/views/contacts/index.html.erb:

<h1>Cache Stats</h1>

<table>
  <tr>
    <th>Metric</th>
    <th>Value</th>
  </tr>
  <tr>
    <td>Cache hits:</td>
    <td><%= @stats['get_hits'] %></td>
  </tr>
  <tr>
    <td>Cache misses:</td>
    <td><%= @stats['get_misses'] %></td>
  </tr>
  <tr>
    <td>Cache flushes:</td>
    <td><%= @stats['cmd_flush'] %></td>
  </tr>
</table>

Commit the results and push to Heroku.

$ git commit -am "Add caching."
$ git push heroku master

Refresh the /contacts page and you’ll see “Cache misses: 1”. This is because you attempted to fetch the 'Contact.all' key, but it wasn’t present. Refresh again and you’ll now see “Cache hits: 1”. This time the 'Contact.all' key was present because it was stored during your previous request.

You can see this effect again if you clear the cache at the heroku console:

$ heroku run console
>> Rails.cache.clear

Expiring the cache

Now that Contact.all is cached, what happens when that table changes? Try adding a new contact and returning to the listing page. You’ll see that your new contact isn’t displayed. Since Contact.all is cached, the old value is still being served. You need a way of expiring cache values when something changes. This can be accomplished with filters in the Contact model.

Add the following code to app/models/contact.rb to the Contact class:

class Contact < ApplicationRecord
  after_save    :expire_contact_all_cache
  after_destroy :expire_contact_all_cache

  def expire_contact_all_cache
    Rails.cache.delete('Contact.all')
  end

  #...

end

Commit these changes and push to Heroku:

$ git commit -am "Expire cache."
$ git push heroku master

Now you can see that every time you save (create or update) or destroy a contact, the Contact.all cache key is deleted. Every time you make one of these changes and return to /contacts, you should see the “Cache misses” count get incremented by 1.

Built-in Rails caching

The examples above explain how to fetch and expire caches explicitly. Conveniently, Rails builds in much of this functionality for you. By setting

config.action_controller.perform_caching = true

in config/environments/production.rb Rails allows you to do fragment, action, and page caching.

Here we just briefly introduce these caching techniques. For more details and other techniques such as russian doll caching, please refer to the Rails Guide on Caching.

Fragment caching

Pages in Rails are generally built from various components. These components can be cached with fragment caching so they do not need to be rebuilt each time the page is requested.

Our /contacts page for example is built from contact components, each showing the name, the email, and 3 actions (show, edit, and destroy). We can cache these fragments by adding the following to @contacts.each loop in app/views/contacts/index.html.erb:

# ...
<% @contacts.each do |contact| %>
  <% cache contact do %>
    # ...
  <% end %>
<% end %>
# ...

Action caching

In addition to fragments, Rails can also cache the whole page with page and action caching. Page caching is more efficient as it allows to completely bypass the Rails stack but it does not work for pages with before filters, such as authentication and it is a bit tricky to set up on Heroku because there is no file storage. Action caching stores objects and views similar to page caching, but it is served by the Rails stack.

To use action caching you need to add the actionpack-action_caching gem to your Gemfile and run bundle install:

gem 'actionpack-action_caching'

To cache the results of the show action, for example, add the following line in app/controllers/contacts_controller.rb:

class ContactsController < ApplicationController
  caches_action :show
  # ...
end

For proper expiration, add the following line in both the update and destroy methods in contacts_controller.rb

def update
  expire_action :action => :show
  # ...
end

def destroy
  expire_action :action => :show
  # ...
end

Note that even if you use action caching, fragment caching remains important. If a page expires, fragment caching makes sure the whole page does not have to be rebuilt from scratch but can use already cached fragments. This technique is similar to russian doll caching.

Session caching

Memcache is a good and fast storage for your non-permanent sessions (for permanent sessions you should use a database). This is true especially on Heroku where dynos each have their own ephemeral filesystem. Using the filesystem to store sessions (default) on Heroku can thus lead to inconsistencies.

To use your cache for session storage create (Rails 5) or edit (Rails 3 and 4) the file config/initializers/session_store.rb to contain:

# Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cache_store, key: '_memcache-example_session'

Further reading and resources

The full source of the application built in this tutorial is freely available for download on GitHub.

  • Rails Guide on Caching
  • Caching Strategies for Rails
  • Using Rack::Cache with Memcached
  • Advance Memcache Usage
  • MemCachier Documentation
  • MemCachier Add-on Page

Keep reading

  • Rails Support

Feedback

Log in to submit feedback.

Using Rack::Cache with Memcached in Rails 3.1+ (Including Rails 4) Caching Strategies for Rails

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2025 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices