Skip to content
This repository

Remove ActiveSupport::Cache::MemCacheStore #6296

Closed
wants to merge 4 commits into from

8 participants

Guillermo Iguaran Rafael Mendonça França Arun Agrawal Jeremy Kemper Tanguy Krotoff Santiago Pastorino Mike Perham Steve Klabnik
Guillermo Iguaran
Owner

memcache-client was deprecated in favor of dalli in 2010, now users should be using dalli_store instead of the old mem_cache_store

Rafael Mendonça França
Owner

:+1:

Arun Agrawal
Collaborator
arunagw commented

Awesome :heart:

Santiago Pastorino spastorino commented on the diff
...ls/app/templates/config/environments/production.rb.tt
@@ -42,7 +42,8 @@
42 42 # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
43 43
44 44 # Use a different cache store in production.
45   - # config.cache_store = :mem_cache_store
  45 + # Add dalli to Gemfile for :dalli_store
  46 + # config.cache_store = :dalli_store
4
Santiago Pastorino Owner
spastorino added a note

Do you need to set the cache_store to :dalli_store? Why the gem doesn't do that automatically?

Guillermo Iguaran Owner

dalli isn't shipped with a railtie to set this config

cc @mperham

Mike Perham
mperham added a note

Just a PR away. :-)

Steve Klabnik Collaborator
steveklabnik added a note

@mperham just sent one ;) :heart:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Jeremy Kemper
Owner
jeremy commented

-1 on removing the cache store entirely. This will make it really painful to upgrade existing apps.

Let's just change the default cache store to Dalli's.

We also need to consider how apps should upgrade cache stores. Existing apps are talking to memcache servers full of marshalled data, and reading it with Dalli won't work. You have to change the cache namespace, or clear it entirely.

The raw API changes slightly too, from a boolean arg to an options hash.

Jeremy Kemper
Owner
jeremy commented

Another option: keep the cache store; just switch the internal lib from memcache-client to dalli.

Steve Klabnik steveklabnik referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Steve Klabnik steveklabnik referenced this pull request in mperham/dalli
Merged

Added railtie to set config.cache_store. #217

Steve Klabnik steveklabnik referenced this pull request from a commit in steveklabnik/dalli
Steve Klabnik steveklabnik Added railtie to set config.cache_store.
This addresses rails/rails#6296
5f9c3bf
Guillermo Iguaran
Owner

@jeremy I'm keeping cache store switching it from memcache-client to dalli. See #6903.

Closing this, see #6903

Tanguy Krotoff

Could you also update the guide "Caching with Rails: An overview": http://edgeguides.rubyonrails.org/caching_with_rails.html#activesupport-cache-memcachestore

Guillermo Iguaran

@tkrotoff done, thanks!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
3  Gemfile
@@ -41,9 +41,6 @@ group :doc do
41 41 gem 'w3c_validators'
42 42 end
43 43
44   -# AS
45   -gem 'memcache-client', '>= 1.8.5'
46   -
47 44 # Add your own local bundler stuff
48 45 local_gemfile = File.dirname(__FILE__) + "/.Gemfile"
49 46 instance_eval File.read local_gemfile if File.exists? local_gemfile
3  activesupport/CHANGELOG.md
Source Rendered
... ... @@ -1,5 +1,8 @@
1 1 ## Rails 4.0.0 (unreleased) ##
2 2
  3 +* Deletes ActiveSupport::Cache::MemCacheStore in favour of
  4 + ActiveSupport::Cache::DalliStore shipped with dalli gem *Guillermo Iguaran*
  5 +
3 6 * `Object#try` can't call private methods. *Vasiliy Ermolovich*
4 7
5 8 * `AS::Callbacks#run_callbacks` remove `key` argument. *Francesco Rodriguez*
11 activesupport/lib/active_support/cache.rb
@@ -15,7 +15,6 @@ module ActiveSupport
15 15 module Cache
16 16 autoload :FileStore, 'active_support/cache/file_store'
17 17 autoload :MemoryStore, 'active_support/cache/memory_store'
18   - autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
19 18 autoload :NullStore, 'active_support/cache/null_store'
20 19
21 20 # These options mean something to all cache implementations. Individual cache
@@ -39,8 +38,8 @@ class << self
39 38 # ActiveSupport::Cache.lookup_store(:memory_store)
40 39 # # => returns a new ActiveSupport::Cache::MemoryStore object
41 40 #
42   - # ActiveSupport::Cache.lookup_store(:mem_cache_store)
43   - # # => returns a new ActiveSupport::Cache::MemCacheStore object
  41 + # ActiveSupport::Cache.lookup_store(:dalli_store)
  42 + # # => returns a new ActiveSupport::Cache::DalliStore object
44 43 #
45 44 # Any additional arguments will be passed to the corresponding cache store
46 45 # class's constructor:
@@ -100,7 +99,7 @@ def retrieve_cache_key(key)
100 99 # An abstract cache store class. There are multiple cache store
101 100 # implementations, each having its own additional features. See the classes
102 101 # under the ActiveSupport::Cache module, e.g.
103   - # ActiveSupport::Cache::MemCacheStore. MemCacheStore is currently the most
  102 + # ActiveSupport::Cache::DalliStore. DalliStore is currently the most
104 103 # popular cache store for large production websites.
105 104 #
106 105 # Some implementations may not support all methods beyond the basic cache
@@ -260,11 +259,11 @@ def self.instrument
260 259 # Internally, #fetch calls #read_entry, and calls #write_entry on a cache miss.
261 260 # +options+ will be passed to the #read and #write calls.
262 261 #
263   - # For example, MemCacheStore's #write method supports the +:raw+
  262 + # For example, DalliStore's #write method supports the +:raw+
264 263 # option, which tells the memcached server to store all values as strings.
265 264 # We can use this option with #fetch too:
266 265 #
267   - # cache = ActiveSupport::Cache::MemCacheStore.new
  266 + # cache = ActiveSupport::Cache::DalliStore.new
268 267 # cache.fetch("foo", :force => true, :raw => true) do
269 268 # :bar
270 269 # end
205 activesupport/lib/active_support/cache/mem_cache_store.rb
... ... @@ -1,205 +0,0 @@
1   -begin
2   - require 'memcache'
3   -rescue LoadError => e
4   - $stderr.puts "You don't have memcache-client installed in your application. Please add it to your Gemfile and run bundle install"
5   - raise e
6   -end
7   -
8   -require 'digest/md5'
9   -
10   -module ActiveSupport
11   - module Cache
12   - # A cache store implementation which stores data in Memcached:
13   - # http://memcached.org/
14   - #
15   - # This is currently the most popular cache store for production websites.
16   - #
17   - # Special features:
18   - # - Clustering and load balancing. One can specify multiple memcached servers,
19   - # and MemCacheStore will load balance between all available servers. If a
20   - # server goes down, then MemCacheStore will ignore it until it comes back up.
21   - #
22   - # MemCacheStore implements the Strategy::LocalCache strategy which implements
23   - # an in-memory cache inside of a block.
24   - class MemCacheStore < Store
25   - module Response # :nodoc:
26   - STORED = "STORED\r\n"
27   - NOT_STORED = "NOT_STORED\r\n"
28   - EXISTS = "EXISTS\r\n"
29   - NOT_FOUND = "NOT_FOUND\r\n"
30   - DELETED = "DELETED\r\n"
31   - end
32   -
33   - ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
34   -
35   - def self.build_mem_cache(*addresses)
36   - addresses = addresses.flatten
37   - options = addresses.extract_options!
38   - addresses = ["localhost:11211"] if addresses.empty?
39   - MemCache.new(addresses, options)
40   - end
41   -
42   - # Creates a new MemCacheStore object, with the given memcached server
43   - # addresses. Each address is either a host name, or a host-with-port string
44   - # in the form of "host_name:port". For example:
45   - #
46   - # ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
47   - #
48   - # If no addresses are specified, then MemCacheStore will connect to
49   - # localhost port 11211 (the default memcached port).
50   - #
51   - # Instead of addresses one can pass in a MemCache-like object. For example:
52   - #
53   - # require 'memcached' # gem install memcached; uses C bindings to libmemcached
54   - # ActiveSupport::Cache::MemCacheStore.new(Memcached::Rails.new("localhost:11211"))
55   - def initialize(*addresses)
56   - addresses = addresses.flatten
57   - options = addresses.extract_options!
58   - super(options)
59   -
60   - if addresses.first.respond_to?(:get)
61   - @data = addresses.first
62   - else
63   - mem_cache_options = options.dup
64   - UNIVERSAL_OPTIONS.each{|name| mem_cache_options.delete(name)}
65   - @data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
66   - end
67   -
68   - extend Strategy::LocalCache
69   - extend LocalCacheWithRaw
70   - end
71   -
72   - # Reads multiple values from the cache using a single call to the
73   - # servers for all keys. Options can be passed in the last argument.
74   - def read_multi(*names)
75   - options = names.extract_options!
76   - options = merged_options(options)
77   - keys_to_names = Hash[names.map{|name| [escape_key(namespaced_key(name, options)), name]}]
78   - raw_values = @data.get_multi(keys_to_names.keys, :raw => true)
79   - values = {}
80   - raw_values.each do |key, value|
81   - entry = deserialize_entry(value)
82   - values[keys_to_names[key]] = entry.value unless entry.expired?
83   - end
84   - values
85   - end
86   -
87   - # Increment a cached value. This method uses the memcached incr atomic
88   - # operator and can only be used on values written with the :raw option.
89   - # Calling it on a value not stored with :raw will initialize that value
90   - # to zero.
91   - def increment(name, amount = 1, options = nil) # :nodoc:
92   - options = merged_options(options)
93   - response = instrument(:increment, name, :amount => amount) do
94   - @data.incr(escape_key(namespaced_key(name, options)), amount)
95   - end
96   - response == Response::NOT_FOUND ? nil : response.to_i
97   - rescue MemCache::MemCacheError
98   - nil
99   - end
100   -
101   - # Decrement a cached value. This method uses the memcached decr atomic
102   - # operator and can only be used on values written with the :raw option.
103   - # Calling it on a value not stored with :raw will initialize that value
104   - # to zero.
105   - def decrement(name, amount = 1, options = nil) # :nodoc:
106   - options = merged_options(options)
107   - response = instrument(:decrement, name, :amount => amount) do
108   - @data.decr(escape_key(namespaced_key(name, options)), amount)
109   - end
110   - response == Response::NOT_FOUND ? nil : response.to_i
111   - rescue MemCache::MemCacheError
112   - nil
113   - end
114   -
115   - # Clear the entire cache on all memcached servers. This method should
116   - # be used with care when shared cache is being used.
117   - def clear(options = nil)
118   - @data.flush_all
119   - end
120   -
121   - # Get the statistics from the memcached servers.
122   - def stats
123   - @data.stats
124   - end
125   -
126   - protected
127   - # Read an entry from the cache.
128   - def read_entry(key, options) # :nodoc:
129   - deserialize_entry(@data.get(escape_key(key), true))
130   - rescue MemCache::MemCacheError => e
131   - logger.error("MemCacheError (#{e}): #{e.message}") if logger
132   - nil
133   - end
134   -
135   - # Write an entry to the cache.
136   - def write_entry(key, entry, options) # :nodoc:
137   - method = options && options[:unless_exist] ? :add : :set
138   - value = options[:raw] ? entry.value.to_s : entry
139   - expires_in = options[:expires_in].to_i
140   - if expires_in > 0 && !options[:raw]
141   - # Set the memcache expire a few minutes in the future to support race condition ttls on read
142   - expires_in += 5.minutes
143   - end
144   - response = @data.send(method, escape_key(key), value, expires_in, options[:raw])
145   - response == Response::STORED
146   - rescue MemCache::MemCacheError => e
147   - logger.error("MemCacheError (#{e}): #{e.message}") if logger
148   - false
149   - end
150   -
151   - # Delete an entry from the cache.
152   - def delete_entry(key, options) # :nodoc:
153   - response = @data.delete(escape_key(key))
154   - response == Response::DELETED
155   - rescue MemCache::MemCacheError => e
156   - logger.error("MemCacheError (#{e}): #{e.message}") if logger
157   - false
158   - end
159   -
160   - private
161   -
162   - # Memcache keys are binaries. So we need to force their encoding to binary
163   - # before applying the regular expression to ensure we are escaping all
164   - # characters properly.
165   - def escape_key(key)
166   - key = key.to_s.dup
167   - key = key.force_encoding("BINARY")
168   - key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
169   - key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
170   - key
171   - end
172   -
173   - def deserialize_entry(raw_value)
174   - if raw_value
175   - entry = Marshal.load(raw_value) rescue raw_value
176   - entry.is_a?(Entry) ? entry : Entry.new(entry)
177   - else
178   - nil
179   - end
180   - end
181   -
182   - # Provide support for raw values in the local cache strategy.
183   - module LocalCacheWithRaw # :nodoc:
184   - protected
185   - def read_entry(key, options)
186   - entry = super
187   - if options[:raw] && local_cache && entry
188   - entry = deserialize_entry(entry.value)
189   - end
190   - entry
191   - end
192   -
193   - def write_entry(key, entry, options) # :nodoc:
194   - retval = super
195   - if options[:raw] && local_cache && retval
196   - raw_entry = Entry.new(entry.value.to_s)
197   - raw_entry.expires_at = entry.expires_at
198   - local_cache.write_entry(key, raw_entry, options)
199   - end
200   - retval
201   - end
202   - end
203   - end
204   - end
205   -end
10 activesupport/test/abstract_unit.rb
@@ -23,15 +23,5 @@
23 23 ENV['NO_RELOAD'] = '1'
24 24 require 'active_support'
25 25
26   -def uses_memcached(test_name)
27   - require 'memcache'
28   - begin
29   - MemCache.new('localhost:11211').stats
30   - yield
31   - rescue MemCache::MemCacheError
32   - $stderr.puts "Skipping #{test_name} tests. Start memcached and try again."
33   - end
34   -end
35   -
36 26 # Show backtraces for deprecated behavior for quicker cleanup.
37 27 ActiveSupport::Deprecation.debug = true
86 activesupport/test/caching_test.rb
@@ -69,7 +69,7 @@ def test_expand_cache_key_of_false
69 69 def test_expand_cache_key_of_true
70 70 assert_equal 'true', ActiveSupport::Cache.expand_cache_key(true)
71 71 end
72   -
  72 +
73 73 def test_expand_cache_key_of_array_like_object
74 74 assert_equal 'foo/bar/baz', ActiveSupport::Cache.expand_cache_key(%w{foo bar baz}.to_enum)
75 75 end
@@ -82,40 +82,6 @@ def test_file_fragment_cache_store
82 82 assert_equal "/path/to/cache/directory", store.cache_path
83 83 end
84 84
85   - def test_mem_cache_fragment_cache_store
86   - MemCache.expects(:new).with(%w[localhost], {})
87   - store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost"
88   - assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
89   - end
90   -
91   - def test_mem_cache_fragment_cache_store_with_given_mem_cache
92   - mem_cache = MemCache.new
93   - MemCache.expects(:new).never
94   - store = ActiveSupport::Cache.lookup_store :mem_cache_store, mem_cache
95   - assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
96   - end
97   -
98   - def test_mem_cache_fragment_cache_store_with_given_mem_cache_like_object
99   - MemCache.expects(:new).never
100   - memcache = Object.new
101   - def memcache.get() true end
102   - store = ActiveSupport::Cache.lookup_store :mem_cache_store, memcache
103   - assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
104   - end
105   -
106   - def test_mem_cache_fragment_cache_store_with_multiple_servers
107   - MemCache.expects(:new).with(%w[localhost 192.168.1.1], {})
108   - store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1'
109   - assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
110   - end
111   -
112   - def test_mem_cache_fragment_cache_store_with_options
113   - MemCache.expects(:new).with(%w[localhost 192.168.1.1], { :timeout => 10 })
114   - store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo', :timeout => 10
115   - assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
116   - assert_equal 'foo', store.options[:namespace]
117   - end
118   -
119 85 def test_object_assigned_fragment_cache_store
120 86 store = ActiveSupport::Cache.lookup_store ActiveSupport::Cache::FileStore.new("/path/to/cache/directory")
121 87 assert_kind_of(ActiveSupport::Cache::FileStore, store)
@@ -693,56 +659,6 @@ def test_write_with_unless_exist
693 659 end
694 660 end
695 661
696   -uses_memcached 'memcached backed store' do
697   - class MemCacheStoreTest < ActiveSupport::TestCase
698   - def setup
699   - @cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :expires_in => 60)
700   - @peek = ActiveSupport::Cache.lookup_store(:mem_cache_store)
701   - @data = @cache.instance_variable_get(:@data)
702   - @cache.clear
703   - @cache.silence!
704   - @cache.logger = ActiveSupport::Logger.new("/dev/null")
705   - end
706   -
707   - include CacheStoreBehavior
708   - include LocalCacheBehavior
709   - include CacheIncrementDecrementBehavior
710   - include EncodedKeyCacheBehavior
711   -
712   - def test_raw_values
713   - cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
714   - cache.clear
715   - cache.write("foo", 2)
716   - assert_equal "2", cache.read("foo")
717   - end
718   -
719   - def test_raw_values_with_marshal
720   - cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
721   - cache.clear
722   - cache.write("foo", Marshal.dump([]))
723   - assert_equal [], cache.read("foo")
724   - end
725   -
726   - def test_local_cache_raw_values
727   - cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
728   - cache.clear
729   - cache.with_local_cache do
730   - cache.write("foo", 2)
731   - assert_equal "2", cache.read("foo")
732   - end
733   - end
734   -
735   - def test_local_cache_raw_values_with_marshal
736   - cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
737   - cache.clear
738   - cache.with_local_cache do
739   - cache.write("foo", Marshal.dump([]))
740   - assert_equal [], cache.read("foo")
741   - end
742   - end
743   - end
744   -end
745   -
746 662 class NullStoreTest < ActiveSupport::TestCase
747 663 def setup
748 664 @cache = ActiveSupport::Cache.lookup_store(:null_store)
3  guides/code/getting_started/config/environments/production.rb
@@ -40,7 +40,8 @@
40 40 # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
41 41
42 42 # Use a different cache store in production.
43   - # config.cache_store = :mem_cache_store
  43 + # Add dalli to Gemfile for :dalli_store
  44 + # config.cache_store = :dalli_store
44 45
45 46 # Enable serving of images, stylesheets, and JavaScripts from an asset server.
46 47 # config.action_controller.asset_host = "http://assets.example.com"
3  railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -42,7 +42,8 @@
42 42 # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
43 43
44 44 # Use a different cache store in production.
45   - # config.cache_store = :mem_cache_store
  45 + # Add dalli to Gemfile for :dalli_store
  46 + # config.cache_store = :dalli_store
46 47
47 48 # Enable serving of images, stylesheets, and JavaScripts from an asset server.
48 49 # config.action_controller.asset_host = "http://assets.example.com"

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.