Skip to content

Commit

Permalink
Cache: Enable compression by default for values > 1kB.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jeremy committed Nov 14, 2017
1 parent b6d5e46 commit ed10016
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 16 deletions.
12 changes: 12 additions & 0 deletions activesupport/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@

*Jeremy Daer*

* 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.

*Jeremy Daer*

* Allow `Range#include?` on TWZ ranges

In #11474 we prevented TWZ ranges being iterated over which matched
Expand Down
20 changes: 9 additions & 11 deletions activesupport/lib/active_support/cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,11 @@ def retrieve_store_class(store)
# cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
# @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
#
# Caches can also store values in a compressed format to save space and
# reduce time spent sending data. Since there is overhead, values must be
# large enough to warrant compression. To turn on compression either pass
# <tt>compress: true</tt> in the initializer or as an option to +fetch+
# or +write+. To specify the threshold at which to compress values, set the
# <tt>:compress_threshold</tt> option. The default threshold is 16K.
# Cached data larger than 1kB are compressed by default. To turn off
# compression, pass <tt>compress: false</tt> to the initializer or to
# individual +fetch+ or +write+ method calls. The 1kB compression
# threshold is configurable with the <tt>:compress_threshold</tt> option,
# specified in bytes.
class Store
cattr_accessor :logger, instance_writer: true

Expand Down Expand Up @@ -218,8 +217,7 @@ def mute
# ask whether you should force a cache write. Otherwise, it's clearer to
# just call <tt>Cache#write</tt>.
#
# Setting <tt>:compress</tt> will store a large cache entry set by the call
# in a compressed format.
# Setting <tt>compress: false</tt> disables compression of the cache entry.
#
# Setting <tt>:expires_in</tt> will set an expiration time on the cache.
# All caches support auto-expiring content after a specified number of
Expand Down Expand Up @@ -664,7 +662,7 @@ def save_block_result_to_cache(name, options)
class Entry # :nodoc:
attr_reader :version

DEFAULT_COMPRESS_LIMIT = 16.kilobytes
DEFAULT_COMPRESS_LIMIT = 1.kilobyte

# Creates a new cache entry for the specified value. Options supported are
# +:compress+, +:compress_threshold+, and +:expires_in+.
Expand Down Expand Up @@ -739,8 +737,8 @@ def dup_value!

private
def should_compress?(value, options)
if value && options[:compress]
compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
if value && options.fetch(:compress, true)
compress_threshold = options.fetch(:compress_threshold, DEFAULT_COMPRESS_LIMIT)
serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize

return true if serialized_value_size >= compress_threshold
Expand Down
10 changes: 10 additions & 0 deletions activesupport/test/cache/behaviors/cache_store_behavior.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ def test_read_and_write_compressed_nil
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
Expand Down
13 changes: 10 additions & 3 deletions activesupport/test/cache/cache_entry_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,23 @@ def test_expired
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
Expand Down
4 changes: 2 additions & 2 deletions guides/source/caching_with_rails.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,9 +366,9 @@ There are some common options used by all cache implementations. These can be pa

* `:namespace` - This option can be used to create a namespace within the cache store. It is especially useful if your application shares a cache with other applications.

* `:compress` - This option can be used to indicate that compression should be used in the cache. This can be useful for transferring large cache entries over a slow network.
* `:compress` - Enabled by default. Compresses cache entries so more data can be stored in the same memory footprint, leading to fewer cache evictions and higher hit rates.

* `:compress_threshold` - This option is used in conjunction with the `:compress` option to indicate a threshold under which cache entries should not be compressed. This defaults to 16 kilobytes.
* `:compress_threshold` - Defaults to 1kB. Cache entries larger than this threshold, specified in bytes, are compressed.

* `:expires_in` - This option sets an expiration time in seconds for the cache entry when it will be automatically removed from the cache.

Expand Down

0 comments on commit ed10016

Please sign in to comment.