From e2dbfe5adac3438e3f8bbd2e2dce2ad39c91221b Mon Sep 17 00:00:00 2001 From: Erich Soares Machado Date: Tue, 3 Oct 2017 00:49:13 +0200 Subject: Fixes ActiveSupport::Cache::FileStore#cleanup bug which prevented it from cleaning up the expired cache keys --- activesupport/test/cache/stores/file_store_test.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activesupport/test/cache') diff --git a/activesupport/test/cache/stores/file_store_test.rb b/activesupport/test/cache/stores/file_store_test.rb index 391ab60b3a..66231b0a82 100644 --- a/activesupport/test/cache/stores/file_store_test.rb +++ b/activesupport/test/cache/stores/file_store_test.rb @@ -118,6 +118,7 @@ class FileStoreTest < ActiveSupport::TestCase assert_not @cache.exist?("foo") assert @cache.exist?("baz") assert @cache.exist?("quux") + assert_equal 2, Dir.glob(File.join(cache_dir, "**")).size end end -- cgit v1.2.3 From 9f8ec3535247ac41a9c92e84ddc7a3b771bc318b Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Wed, 17 May 2017 12:09:34 -0700 Subject: Built-in Redis cache store * Supports vanilla Redis, hiredis, and Redis::Distributed. * Supports Memcached-like sharding across Redises with Redis::Distributed. * Fault tolerant. If the Redis server is unavailable, no exceptions are raised. Cache fetches are treated as misses and writes are dropped. * Local cache. Hot in-memory primary cache within block/middleware scope. * `read_/write_multi` support for Redis mget/mset. Use Redis::Distributed 4.0.1+ for distributed mget support. * `delete_matched` support for Redis KEYS globs. --- .../cache_increment_decrement_behavior.rb | 8 +- .../test/cache/behaviors/local_cache_behavior.rb | 6 +- .../test/cache/stores/redis_cache_store_test.rb | 151 +++++++++++++++++++++ 3 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 activesupport/test/cache/stores/redis_cache_store_test.rb (limited to 'activesupport/test/cache') diff --git a/activesupport/test/cache/behaviors/cache_increment_decrement_behavior.rb b/activesupport/test/cache/behaviors/cache_increment_decrement_behavior.rb index 2fa2d7af88..16b7abc679 100644 --- a/activesupport/test/cache/behaviors/cache_increment_decrement_behavior.rb +++ b/activesupport/test/cache/behaviors/cache_increment_decrement_behavior.rb @@ -8,7 +8,9 @@ module CacheIncrementDecrementBehavior assert_equal 2, @cache.read("foo").to_i assert_equal 3, @cache.increment("foo") assert_equal 3, @cache.read("foo").to_i - assert_nil @cache.increment("bar") + + missing = @cache.increment("bar") + assert(missing.nil? || missing == 1) end def test_decrement @@ -18,6 +20,8 @@ module CacheIncrementDecrementBehavior assert_equal 2, @cache.read("foo").to_i assert_equal 1, @cache.decrement("foo") assert_equal 1, @cache.read("foo").to_i - assert_nil @cache.decrement("bar") + + missing = @cache.decrement("bar") + assert(missing.nil? || missing == -1) end end diff --git a/activesupport/test/cache/behaviors/local_cache_behavior.rb b/activesupport/test/cache/behaviors/local_cache_behavior.rb index 8dec8090b1..f7302df4c8 100644 --- a/activesupport/test/cache/behaviors/local_cache_behavior.rb +++ b/activesupport/test/cache/behaviors/local_cache_behavior.rb @@ -20,7 +20,11 @@ module LocalCacheBehavior end def test_cleanup_clears_local_cache_but_not_remote_cache - skip unless @cache.class.instance_methods(false).include?(:cleanup) + begin + @cache.cleanup + rescue NotImplementedError + skip + end @cache.with_local_cache do @cache.write("foo", "bar") diff --git a/activesupport/test/cache/stores/redis_cache_store_test.rb b/activesupport/test/cache/stores/redis_cache_store_test.rb new file mode 100644 index 0000000000..988de9207f --- /dev/null +++ b/activesupport/test/cache/stores/redis_cache_store_test.rb @@ -0,0 +1,151 @@ +# frozen_string_literal: true + +require "abstract_unit" +require "active_support/cache" +require "active_support/cache/redis_cache_store" +require_relative "../behaviors" + +module ActiveSupport::Cache::RedisCacheStoreTests + class LookupTest < ActiveSupport::TestCase + test "may be looked up as :redis_cache_store" do + assert_kind_of ActiveSupport::Cache::RedisCacheStore, + ActiveSupport::Cache.lookup_store(:redis_cache_store) + end + end + + class InitializationTest < ActiveSupport::TestCase + test "omitted URL uses Redis client with default settings" do + assert_called_with Redis, :new, [ + url: nil, + connect_timeout: 20, read_timeout: 1, write_timeout: 1, + reconnect_attempts: 0, + ] do + build + end + end + + test "no URLs uses Redis client with default settings" do + assert_called_with Redis, :new, [ + url: nil, + connect_timeout: 20, read_timeout: 1, write_timeout: 1, + reconnect_attempts: 0, + ] do + build url: [] + end + end + + test "singular URL uses Redis client" do + assert_called_with Redis, :new, [ + url: "redis://localhost:6379/0", + connect_timeout: 20, read_timeout: 1, write_timeout: 1, + reconnect_attempts: 0, + ] do + build url: "redis://localhost:6379/0" + end + end + + test "one URL uses Redis client" do + assert_called_with Redis, :new, [ + url: "redis://localhost:6379/0", + connect_timeout: 20, read_timeout: 1, write_timeout: 1, + reconnect_attempts: 0, + ] do + build url: %w[ redis://localhost:6379/0 ] + end + end + + test "multiple URLs uses Redis::Distributed client" do + assert_called_with Redis, :new, [ + [ url: "redis://localhost:6379/0", + connect_timeout: 20, read_timeout: 1, write_timeout: 1, + reconnect_attempts: 0 ], + [ url: "redis://localhost:6379/1", + connect_timeout: 20, read_timeout: 1, write_timeout: 1, + reconnect_attempts: 0 ], + ], returns: Redis.new do + @cache = build url: %w[ redis://localhost:6379/0 redis://localhost:6379/1 ] + assert_kind_of ::Redis::Distributed, @cache.redis + end + end + + test "block argument uses yielded client" do + block = -> { :custom_redis_client } + assert_called block, :call do + build redis: block + end + end + + private + def build(**kwargs) + ActiveSupport::Cache::RedisCacheStore.new(**kwargs).tap do |cache| + cache.redis + end + end + end + + class StoreTest < ActiveSupport::TestCase + setup do + @namespace = "namespace" + + @cache = ActiveSupport::Cache::RedisCacheStore.new(timeout: 0.1, namespace: @namespace, expires_in: 60) + #@cache.logger = Logger.new($stdout) # For test debugging + + # For LocalCacheBehavior tests + @peek = ActiveSupport::Cache::RedisCacheStore.new(timeout: 0.1, namespace: @namespace) + end + + teardown do + @cache.clear + @cache.redis.disconnect! + end + end + + class RedisCacheStoreCommonBehaviorTest < StoreTest + include CacheStoreBehavior + include CacheStoreVersionBehavior + include LocalCacheBehavior + include CacheIncrementDecrementBehavior + include AutoloadingCacheBehavior + end + + # Separate test class so we can omit the namespace which causes expected, + # appropriate complaints about incompatible string encodings. + class KeyEncodingSafetyTest < StoreTest + include EncodedKeyCacheBehavior + + setup do + @cache = ActiveSupport::Cache::RedisCacheStore.new(timeout: 0.1) + @cache.logger = nil + end + end + + class StoreAPITest < StoreTest + end + + class FailureSafetyTest < StoreTest + test "fetch read failure returns nil" do + end + + test "fetch read failure does not attempt to write" do + end + + test "write failure returns nil" do + end + end + + class DeleteMatchedTest < StoreTest + test "deletes keys matching glob" do + @cache.write("foo", "bar") + @cache.write("fu", "baz") + @cache.delete_matched("foo*") + assert !@cache.exist?("foo") + assert @cache.exist?("fu") + end + + test "fails with regexp matchers" do + assert_raise ArgumentError do + @cache.delete_matched(/OO/i) + end + end + end +end -- cgit v1.2.3 From ed100166874fb4a542c5aaba933a4cca5ed72269 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Mon, 13 Nov 2017 19:16:53 -0700 Subject: Cache: Enable compression by default for values > 1kB. Compression has long been available, but opt-in and at a 16kB threshold. It wasn't enabled by default due to CPU cost. Today it's cheap and typical cache data is eminently compressible, such as HTML or JSON fragments. Compression dramatically reduces Memcached/Redis mem usage, which means the same cache servers can store more data, which means higher hit rates. To disable compression, pass `compress: false` to the initializer. --- activesupport/test/cache/behaviors/cache_store_behavior.rb | 10 ++++++++++ activesupport/test/cache/cache_entry_test.rb | 13 ++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'activesupport/test/cache') diff --git a/activesupport/test/cache/behaviors/cache_store_behavior.rb b/activesupport/test/cache/behaviors/cache_store_behavior.rb index 582e902f72..73a9b2a71c 100644 --- a/activesupport/test/cache/behaviors/cache_store_behavior.rb +++ b/activesupport/test/cache/behaviors/cache_store_behavior.rb @@ -146,6 +146,16 @@ module CacheStoreBehavior assert_nil @cache.read("foo") end + def test_read_and_write_uncompressed_small_data + @cache.write("foo", "bar", compress: false) + assert_equal "bar", @cache.read("foo") + end + + def test_read_and_write_uncompressed_nil + @cache.write("foo", nil, compress: false) + assert_nil @cache.read("foo") + end + def test_cache_key obj = Object.new def obj.cache_key diff --git a/activesupport/test/cache/cache_entry_test.rb b/activesupport/test/cache/cache_entry_test.rb index 51b214ad8f..80ff7ad564 100644 --- a/activesupport/test/cache/cache_entry_test.rb +++ b/activesupport/test/cache/cache_entry_test.rb @@ -14,16 +14,23 @@ class CacheEntryTest < ActiveSupport::TestCase end end - def test_compress_values + def test_compressed_values value = "value" * 100 entry = ActiveSupport::Cache::Entry.new(value, compress: true, compress_threshold: 1) assert_equal value, entry.value assert(value.bytesize > entry.size, "value is compressed") end - def test_non_compress_values + def test_compressed_by_default value = "value" * 100 - entry = ActiveSupport::Cache::Entry.new(value) + entry = ActiveSupport::Cache::Entry.new(value, compress_threshold: 1) + assert_equal value, entry.value + assert(value.bytesize > entry.size, "value is compressed") + end + + def test_uncompressed_values + value = "value" * 100 + entry = ActiveSupport::Cache::Entry.new(value, compress: false) assert_equal value, entry.value assert_equal value.bytesize, entry.size end -- cgit v1.2.3 From b22ee64b5b30c6d5039c292235e10b24b1057f6d Mon Sep 17 00:00:00 2001 From: Takumasa Ochi Date: Sun, 19 Nov 2017 03:50:59 +0900 Subject: MemCacheStore: Support expiring counters Support `expires_in` in `ActiveSupport::Cache::MemCacheStore#increment` and `#decrement`. Closes #30716. --- activesupport/test/cache/stores/mem_cache_store_test.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'activesupport/test/cache') diff --git a/activesupport/test/cache/stores/mem_cache_store_test.rb b/activesupport/test/cache/stores/mem_cache_store_test.rb index 1b73fb65eb..99624caf8a 100644 --- a/activesupport/test/cache/stores/mem_cache_store_test.rb +++ b/activesupport/test/cache/stores/mem_cache_store_test.rb @@ -57,6 +57,22 @@ class MemCacheStoreTest < ActiveSupport::TestCase end end + def test_increment_expires_in + cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, raw: true) + cache.clear + assert_called_with cache.instance_variable_get(:@data), :incr, [ "foo", 1, 60 ] do + cache.increment("foo", 1, expires_in: 60) + end + end + + def test_decrement_expires_in + cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, raw: true) + cache.clear + assert_called_with cache.instance_variable_get(:@data), :decr, [ "foo", 1, 60 ] do + cache.decrement("foo", 1, expires_in: 60) + end + end + def test_local_cache_raw_values_with_marshal cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, raw: true) cache.clear -- cgit v1.2.3