Yes, you can do this with Rails. You need to create a second cache and make it available in your app as a global variable, then call the appropriate cache depending on the context. Each cache is assigned its own chunk of memory (32 MB by default) and if one cache fills up it will not affect the other cache. This is done with ActiveSupport::Cache::MemoryStore.new
.
I will demonstrate that the two caches do not impact each other:
First, generate two text files that will be used to test the cache, one 10 MB and one 30 MB:
dd if=/dev/zero of=10M bs=1m count=10
dd if=/dev/zero of=30M bs=1m count=30
Open a Rails console and read these into strings:
ten = File.read("10M"); 0
thirty = File.read("30M"); 0
Store ten
in the cache:
Rails.cache.fetch("ten") { ten }; 0
Confirm the data was cached:
Rails.cache.fetch("ten")[0..10]
=> "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
Store thirty
in the cache:
Rails.cache.fetch("thirty") { thirty }; 0
Confirm this was not saved (too large to save in the cache when expanded as a string):
Rails.cache.fetch("thirty")[0..10]
NoMethodError: undefined method `[]' for nil:NilClass
Confirm this has busted the entire cache:
Rails.cache.fetch("ten")[0..10]
NoMethodError: undefined method `[]' for nil:NilClass
Now create a second cache and confirm it behaves identically to the original cache:
store = ActiveSupport::Cache::MemoryStore.new
store.fetch("ten") { ten }; 0
store.fetch("ten")[0..10]
=> "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
store.fetch("thirty") { thirty }; 0
store.fetch("thirty")[0..10]
NoMethodError: undefined method `[]' for nil:NilClass
store.fetch("ten")[0..10]
NoMethodError: undefined method `[]' for nil:NilClass
Now there are two empty caches: store
and Rails.cache
. Let's confirm they are independent:
Rails.cache.fetch("ten") { ten }; 0
Rails.cache.fetch("ten")[0..10]
=> "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
store.fetch("thirty") { thirty }; 0 # bust the `store' cache
Rails.cache.fetch("ten")[0..10]
=> "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
If the two caches interfered then the last store.fetch
call would have busted both caches. It only busts store
.
To implement a second cache in your app, create an initializer config/initializers/cache.rb
and add:
$cache = ActiveSupport::Cache::MemoryStore.new
Call the new cache in your code the same way you would Rails.cache
:
$cache.fetch("foo") { "bar" }
Some of these details were taken from this answer. The new cache supports additional options; check MemoryStore and Caching with Rails for more information on customizing the cache.
This solution will work for small apps. Note this comment from the MemoryStore docs:
If you're running multiple Ruby on Rails server processes (which is the case if you're using mongrel_cluster or Phusion Passenger), then this means that Rails server process instances won't be able to share cache data with each other and this may not be the most appropriate cache in that scenario.