1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge pull request #40420 from sambostock/use-dalli-fallback

Use `$MEMCACHE_SERVERS` as `MemCacheStore` fallback
This commit is contained in:
Rafael França 2020-10-21 01:15:03 -04:00 committed by GitHub
commit e66e05719f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 17 deletions

View file

@ -1,3 +1,19 @@
* `ActiveSupport::Cache::MemCacheStore` now checks `ENV["MEMCACHE_SERVERS"]` before falling back to `"localhost:11211"` if configured without any addresses.
```ruby
config.cache_store = :mem_cache_store
# is now equivalent to
config.cache_store = :mem_cache_store, ENV["MEMCACHE_SERVERS"] || "localhost:11211"
# instead of
config.cache_store = :mem_cache_store, "localhost:11211" # ignores ENV["MEMCACHE_SERVERS"]
```
*Sam Bostock*
* `ActiveSupport::Subscriber#attach_to` now accepts an `inherit_all:` argument. When set to true,
it allows a subscriber to receive events for methods defined in the subscriber's ancestor class(es).

View file

@ -53,16 +53,18 @@ module ActiveSupport
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
# Creates a new Dalli::Client instance with specified addresses and options.
# By default address is equal localhost:11211.
# If no addresses are provided, we give nil to Dalli::Client, so it uses its fallbacks:
# - ENV["MEMCACHE_SERVERS"] (if defined)
# - "127.0.0.1:11211" (otherwise)
#
# ActiveSupport::Cache::MemCacheStore.build_mem_cache
# # => #<Dalli::Client:0x007f98a47d2028 @servers=["localhost:11211"], @options={}, @ring=nil>
# # => #<Dalli::Client:0x007f98a47d2028 @servers=["127.0.0.1:11211"], @options={}, @ring=nil>
# ActiveSupport::Cache::MemCacheStore.build_mem_cache('localhost:10290')
# # => #<Dalli::Client:0x007f98a47b3a60 @servers=["localhost:10290"], @options={}, @ring=nil>
def self.build_mem_cache(*addresses) # :nodoc:
addresses = addresses.flatten
options = addresses.extract_options!
addresses = ["localhost:11211"] if addresses.empty?
addresses = nil if addresses.empty?
pool_options = retrieve_pool_options(options)
if pool_options.empty?
@ -79,8 +81,8 @@ module ActiveSupport
#
# ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
#
# If no addresses are specified, then MemCacheStore will connect to
# localhost port 11211 (the default memcached port).
# If no addresses are provided, but ENV['MEMCACHE_SERVERS'] is defined, it will be used instead. Otherwise,
# MemCacheStore will connect to localhost:11211 (the default memcached port).
def initialize(*addresses)
addresses = addresses.flatten
options = addresses.extract_options!

View file

@ -45,7 +45,6 @@ class MemCacheStoreTest < ActiveSupport::TestCase
@namespace = "test-#{SecureRandom.hex}"
@cache = lookup_store(expires_in: 60)
@peek = lookup_store
@data = @cache.instance_variable_get(:@data)
@cache.silence!
@cache.logger = ActiveSupport::Logger.new(File::NULL)
end
@ -64,7 +63,6 @@ class MemCacheStoreTest < ActiveSupport::TestCase
# Overrides test from LocalCacheBehavior in order to stub out the cache clear
# and replace it with a delete.
def test_clear_also_clears_local_cache
client = @cache.instance_variable_get(:@data)
key = "#{@namespace}:foo"
client.stub(:flush_all, -> { client.delete(key) }) do
super
@ -102,14 +100,14 @@ class MemCacheStoreTest < ActiveSupport::TestCase
def test_increment_expires_in
cache = lookup_store(raw: true, namespace: nil)
assert_called_with cache.instance_variable_get(:@data), :incr, [ "foo", 1, 60 ] do
assert_called_with client(cache), :incr, [ "foo", 1, 60 ] do
cache.increment("foo", 1, expires_in: 60)
end
end
def test_decrement_expires_in
cache = lookup_store(raw: true, namespace: nil)
assert_called_with cache.instance_variable_get(:@data), :decr, [ "foo", 1, 60 ] do
assert_called_with client(cache), :decr, [ "foo", 1, 60 ] do
cache.decrement("foo", 1, expires_in: 60)
end
end
@ -164,18 +162,47 @@ class MemCacheStoreTest < ActiveSupport::TestCase
def test_unless_exist_expires_when_configured
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store)
assert_called_with cache.instance_variable_get(:@data), :add, [ "foo", ActiveSupport::Cache::Entry, 1, Hash ] do
assert_called_with client(cache), :add, [ "foo", ActiveSupport::Cache::Entry, 1, Hash ] do
cache.write("foo", "bar", expires_in: 1, unless_exist: true)
end
end
def test_uses_provided_dalli_client_if_present
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, Dalli::Client.new("custom_host"))
assert_equal ["custom_host"], servers(cache)
end
def test_forwards_string_addresses_if_present
expected_addresses = ["first", "second"]
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, expected_addresses)
assert_equal expected_addresses, servers(cache)
end
def test_falls_back_to_localhost_if_no_address_provided_and_memcache_servers_undefined
with_memcache_servers_environment_variable(nil) do
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store)
assert_equal ["127.0.0.1:11211"], servers(cache)
end
end
def test_falls_back_to_localhost_if_no_address_provided_and_memcache_servers_defined
with_memcache_servers_environment_variable("custom_host") do
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store)
assert_equal ["custom_host"], servers(cache)
end
end
private
def random_string(length)
(0...length).map { (65 + rand(26)).chr }.join
end
def store
[:mem_cache_store, ENV["MEMCACHE_SERVERS"] || "localhost:11211"]
[:mem_cache_store]
end
def emulating_latency
@ -197,4 +224,24 @@ class MemCacheStoreTest < ActiveSupport::TestCase
Dalli.send(:remove_const, :Server)
Dalli.const_set(:Server, old_server)
end
def servers(cache = @cache)
client(cache).instance_variable_get(:@servers)
end
def client(cache = @cache)
cache.instance_variable_get(:@data)
end
def with_memcache_servers_environment_variable(value)
original_value = ENV["MEMCACHE_SERVERS"]
ENV["MEMCACHE_SERVERS"] = value
yield
ensure
if original_value.nil?
ENV.delete("MEMCACHE_SERVERS")
else
ENV["MEMCACHE_SERVERS"] = original_value
end
end
end

View file

@ -1,3 +1,7 @@
* Updated `ActiveSupport::Cache::MemCacheStore` docs to reflect support for `$MEMCACHE_SERVERS`.
*Sam Bostock*
* Use Bookstore as a unified use-case for all examples in Active Record Query Interface Guide.
*Ashley Engelund*, *Vipul A M*

View file

@ -453,17 +453,22 @@ no explicit `config.cache_store` is supplied.
This cache store uses Danga's `memcached` server to provide a centralized cache for your application. Rails uses the bundled `dalli` gem by default. This is currently the most popular cache store for production websites. It can be used to provide a single, shared cache cluster with very high performance and redundancy.
When initializing the cache, you need to specify the addresses for all
memcached servers in your cluster. If none are specified, it will assume
memcached is running on localhost on the default port, but this is not an ideal
setup for larger sites.
The `write` and `fetch` methods on this cache accept two additional options that take advantage of features specific to memcached. You can specify `:raw` to send a value directly to the server with no serialization. The value must be a string or number. You can use memcached direct operations like `increment` and `decrement` only on raw values. You can also specify `:unless_exist` if you don't want memcached to overwrite an existing entry.
When initializing the cache, you should specify the addresses for all memcached servers in your cluster, or ensure the `MEMCACHE_SERVERS` environment variable has been set appropriately.
```ruby
config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com"
```
If neither are specified, it will assume memcached is running on localhost on the default port (`127.0.0.1:11211`), but this is not an ideal setup for larger sites.
```ruby
config.cache_store = :mem_cache_store # Will fallback to $MEMCACHE_SERVERS, then 127.0.0.1:11211
```
See the [`Dalli::Client` documentation](https://www.rubydoc.info/github/mperham/dalli/Dalli%2FClient:initialize) for supported address types.
The `write` and `fetch` methods on this cache accept two additional options that take advantage of features specific to memcached. You can specify `:raw` to send a value directly to the server with no serialization. The value must be a string or number. You can use memcached direct operations like `increment` and `decrement` only on raw values. You can also specify `:unless_exist` if you don't want memcached to overwrite an existing entry.
### ActiveSupport::Cache::RedisCacheStore
The Redis cache store takes advantage of Redis support for automatic eviction