How to use multiple caches in rails? (for real)
Asked Answered
V

2

19

I'd like to use 2 caches -- the in memory default one and a memcache one, though abstractly it shouldn't matter (I think) which two.

The in memory default one is where I want to load small and rarely changing data. I've been using the memory one to date. I keep a bunch of 'domain data' type stuff from the database in there, I also have some small data from external sources that I refresh every 15 min - 1 hour.

I recently added memcache because I'm now serving up some larger assets. Sort of complex how I got into this, but these are larger ~kilobytes, relatively small in quantity (hundreds), and highly cacheable -- they change, but a refresh once per hour is probably too much. This set might grow, but it's shared across all hosts. Refreshes are expensive.

The first set of data has been using the default memory cache for a while now, and has been well-behaved. Memcache is perfect for the second set of data.

I've tuned memcache, and it's working great for the second set of data. The problem is that because of my existing code that was done 'thinking' it was in local memory, I'm doing several trips to memcache per request, which is increasing my latency.

So, I want to use 2 caches. Thoughts?

(note: memcache is running on different machine(s) than my server. Even if I ran it locally, I have a fleet of hosts so it wouldn't be local to all. Also, I want to avoid needing to just get bigger machines. Even though I probably could solve this problem by making the memory bigger and just using the in memory (the data really isn't that big), this doesn't solve the problem as I scale, so it will just be kicking the can.)

Vender answered 26/10, 2011 at 5:27 Comment(0)
W
21

ActiveSupport::Cache::MemoryStore is what you want to use. Rails.cache uses either MemoryStore, FileStore or in my case DalliStore :-)

You can have global instance of ActiveSupport::Cache::MemoryStore and use it or create a class with a singleton pattern that holds this object (cleaner). Set Rails.cache to the other cache store and use this singleton for MemoryStore

Below is this class:

module Caching
  class MemoryCache
      include Singleton

      # create a private instance of MemoryStore
      def initialize
        @memory_store = ActiveSupport::Cache::MemoryStore.new
      end

      # this will allow our MemoryCache to be called just like Rails.cache
      # every method passed to it will be passed to our MemoryStore
      def method_missing(m, *args, &block)
        @memory_store.send(m, *args, &block)
      end
  end
end

This is how to use it:

Caching::MemoryCache.instance.write("foo", "bar")
=> true
Caching::MemoryCache.instance.read("foo")
=> "bar"
Caching::MemoryCache.instance.clear
=> 0
Caching::MemoryCache.instance.read("foo")
=> nil
Caching::MemoryCache.instance.write("foo1", "bar1")
=> true
Caching::MemoryCache.instance.write("foo2", "bar2")
=> true
Caching::MemoryCache.instance.read_multi("foo1", "foo2")
=> {"foo1"=>"bar1", "foo2"=>"bar2"}
Winch answered 21/3, 2012 at 15:48 Comment(0)
P
10

In an initializer you can just put:

MyMemoryCache = ActiveSupport::Cache::MemoryStore.new

Then you can use it like this:

MyMemoryCache.fetch('my-key', 'my-value')

and so on.

Note that if it's just for performance optimization (and depends on time expiration), it may not be a bad idea to disable it in your test environment, as follows:

if Rails.env.test?
  MyMemoryCache = ActiveSupport::Cache::NullStore.new
else
  MyMemoryCache = ActiveSupport::Cache::MemoryStore.new
end

Rails already provides this by allowing you to set different values config.cache_store in your environment initializers.

Poaceous answered 14/12, 2014 at 6:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.