aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
authorGodfrey Chan <godfreykfc@gmail.com>2018-04-11 21:38:02 -0700
committerGodfrey Chan <godfreykfc@gmail.com>2018-04-11 21:43:51 -0700
commit84b1feeaff94a598b335c5d3f73c4f74f489a165 (patch)
tree5f97c2846f99622983ce1e041367a1260cb97a78 /activesupport
parent7622b85a300a1863ece44eb429ddabdadbee7f77 (diff)
downloadrails-84b1feeaff94a598b335c5d3f73c4f74f489a165.tar.gz
rails-84b1feeaff94a598b335c5d3f73c4f74f489a165.tar.bz2
rails-84b1feeaff94a598b335c5d3f73c4f74f489a165.zip
Add failing test for compression bug
On Rails 5.2, when compression is enabled (which it is by default), the actual value being written to the underlying storage is actually _bigger_ than the uncompressed raw value. This is because the `@marshaled_value` instance variable (typically) gets serialized with the entry object, which is then written to the underlying storage, essentially double-storing every value (once uncompressed, once possibly compressed). This regression was introduced in #32254.
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/test/cache/behaviors/cache_store_behavior.rb130
-rw-r--r--activesupport/test/cache/cache_entry_test.rb21
-rw-r--r--activesupport/test/cache/stores/memory_store_test.rb10
3 files changed, 123 insertions, 38 deletions
diff --git a/activesupport/test/cache/behaviors/cache_store_behavior.rb b/activesupport/test/cache/behaviors/cache_store_behavior.rb
index efb57d34a2..84a3648ad0 100644
--- a/activesupport/test/cache/behaviors/cache_store_behavior.rb
+++ b/activesupport/test/cache/behaviors/cache_store_behavior.rb
@@ -141,29 +141,92 @@ module CacheStoreBehavior
end
end
- def test_read_and_write_compressed_small_data
- @cache.write("foo", "bar", compress: true)
- assert_equal "bar", @cache.read("foo")
+ # Use strings that are guarenteed to compress well, so we can easily tell if
+ # the compression kicked in or not.
+ SMALL_STRING = "0" * 100
+ LARGE_STRING = "0" * 2.kilobytes
+
+ SMALL_OBJECT = { data: SMALL_STRING }
+ LARGE_OBJECT = { data: LARGE_STRING }
+
+ def test_nil_with_default_compression_settings
+ assert_uncompressed(nil)
end
- def test_read_and_write_compressed_large_data
- @cache.write("foo", "bar", compress: true, compress_threshold: 2)
- assert_equal "bar", @cache.read("foo")
+ def test_nil_with_compress_true
+ assert_uncompressed(nil, compress: true)
end
- def test_read_and_write_compressed_nil
- @cache.write("foo", nil, compress: true)
- assert_nil @cache.read("foo")
+ def test_nil_with_compress_false
+ assert_uncompressed(nil, compress: false)
end
- def test_read_and_write_uncompressed_small_data
- @cache.write("foo", "bar", compress: false)
- assert_equal "bar", @cache.read("foo")
+ def test_nil_with_compress_low_compress_threshold
+ assert_uncompressed(nil, compress: true, compress_threshold: 2)
end
- def test_read_and_write_uncompressed_nil
- @cache.write("foo", nil, compress: false)
- assert_nil @cache.read("foo")
+ def test_small_string_with_default_compression_settings
+ assert_uncompressed(SMALL_STRING)
+ end
+
+ def test_small_string_with_compress_true
+ assert_uncompressed(SMALL_STRING, compress: true)
+ end
+
+ def test_small_string_with_compress_false
+ assert_uncompressed(SMALL_STRING, compress: false)
+ end
+
+ def test_small_string_with_low_compress_threshold
+ assert_compressed(SMALL_STRING, compress: true, compress_threshold: 2)
+ end
+
+ def test_small_object_with_default_compression_settings
+ assert_uncompressed(SMALL_OBJECT)
+ end
+
+ def test_small_object_with_compress_true
+ assert_uncompressed(SMALL_OBJECT, compress: true)
+ end
+
+ def test_small_object_with_compress_false
+ assert_uncompressed(SMALL_OBJECT, compress: false)
+ end
+
+ def test_small_object_with_low_compress_threshold
+ assert_compressed(SMALL_OBJECT, compress: true, compress_threshold: 1)
+ end
+
+ def test_large_string_with_default_compression_settings
+ assert_compressed(LARGE_STRING)
+ end
+
+ def test_large_string_with_compress_true
+ assert_compressed(LARGE_STRING, compress: true)
+ end
+
+ def test_large_string_with_compress_false
+ assert_uncompressed(LARGE_STRING, compress: false)
+ end
+
+ def test_large_string_with_high_compress_threshold
+ assert_uncompressed(LARGE_STRING, compress: true, compress_threshold: 1.megabyte)
+ end
+
+ def test_large_object_with_default_compression_settings
+ assert_compressed(LARGE_OBJECT)
+ end
+
+ def test_large_object_with_compress_true
+ assert_compressed(LARGE_OBJECT, compress: true)
+ end
+
+ def test_large_object_with_compress_false
+ assert_uncompressed(LARGE_OBJECT, compress: false)
+ end
+
+ def test_large_object_with_high_compress_threshold
+ assert_uncompressed(LARGE_OBJECT, compress: true, compress_threshold: 1.megabyte)
end
def test_cache_key
@@ -359,4 +422,41 @@ module CacheStoreBehavior
ensure
ActiveSupport::Notifications.unsubscribe "cache_read.active_support"
end
+
+ private
+
+ def assert_compressed(value, **options)
+ assert_compression(true, value, **options)
+ end
+
+ def assert_uncompressed(value, **options)
+ assert_compression(false, value, **options)
+ end
+
+ def assert_compression(should_compress, value, **options)
+ freeze_time do
+ @cache.write("actual", value, **options)
+ @cache.write("uncompressed", value, **options, compress: false)
+ end
+
+ if value.nil?
+ assert_nil @cache.read("actual")
+ assert_nil @cache.read("uncompressed")
+ else
+ assert_equal value, @cache.read("actual")
+ assert_equal value, @cache.read("uncompressed")
+ end
+
+ actual_entry = @cache.send(:read_entry, @cache.send(:normalize_key, "actual", {}), {})
+ uncompressed_entry = @cache.send(:read_entry, @cache.send(:normalize_key, "uncompressed", {}), {})
+
+ actual_size = Marshal.dump(actual_entry).bytesize
+ uncompressed_size = Marshal.dump(uncompressed_entry).bytesize
+
+ if should_compress
+ assert_operator actual_size, :<, uncompressed_size, "value should be compressed"
+ else
+ assert_equal uncompressed_size, actual_size, "value should not be compressed"
+ end
+ end
end
diff --git a/activesupport/test/cache/cache_entry_test.rb b/activesupport/test/cache/cache_entry_test.rb
index 80ff7ad564..d7baaa5c72 100644
--- a/activesupport/test/cache/cache_entry_test.rb
+++ b/activesupport/test/cache/cache_entry_test.rb
@@ -13,25 +13,4 @@ class CacheEntryTest < ActiveSupport::TestCase
assert entry.expired?, "entry is expired"
end
end
-
- 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_compressed_by_default
- value = "value" * 100
- 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
end
diff --git a/activesupport/test/cache/stores/memory_store_test.rb b/activesupport/test/cache/stores/memory_store_test.rb
index 72fafc187b..8fe8384fb0 100644
--- a/activesupport/test/cache/stores/memory_store_test.rb
+++ b/activesupport/test/cache/stores/memory_store_test.rb
@@ -6,8 +6,7 @@ require_relative "../behaviors"
class MemoryStoreTest < ActiveSupport::TestCase
def setup
- @record_size = ActiveSupport::Cache.lookup_store(:memory_store).send(:cached_size, 1, ActiveSupport::Cache::Entry.new("aaaaaaaaaa"))
- @cache = ActiveSupport::Cache.lookup_store(:memory_store, expires_in: 60, size: @record_size * 10 + 1)
+ @cache = ActiveSupport::Cache.lookup_store(:memory_store, expires_in: 60)
end
include CacheStoreBehavior
@@ -15,6 +14,13 @@ class MemoryStoreTest < ActiveSupport::TestCase
include CacheDeleteMatchedBehavior
include CacheIncrementDecrementBehavior
include CacheInstrumentationBehavior
+end
+
+class MemoryStorePruningTest < ActiveSupport::TestCase
+ def setup
+ @record_size = ActiveSupport::Cache.lookup_store(:memory_store).send(:cached_size, 1, ActiveSupport::Cache::Entry.new("aaaaaaaaaa"))
+ @cache = ActiveSupport::Cache.lookup_store(:memory_store, expires_in: 60, size: @record_size * 10 + 1)
+ end
def test_prune_size
@cache.write(1, "aaaaaaaaaa") && sleep(0.001)