Fix ActiveSupport::Cache compression#32539
Conversation
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 rails#32254.
(See previous commit for a description of the issue)
|
Ready for review! |
Fix ActiveSupport::Cache compression
|
Thank you! As always, outstanding PR and investigation. |
| def compress! | ||
| @value = Zlib::Deflate.deflate(marshaled_value) | ||
| @compressed = true | ||
| defined?(@compressed) |
There was a problem hiding this comment.
was it changed intentionally since defined?(@compressed) ? @compressed : false looks correct?
There was a problem hiding this comment.
Yes. Ruby semantics of query methods don't require us to return false or true so nil is totally fine and inline with our guidelines.
There was a problem hiding this comment.
@rafaelfranca
Ruby semantics is fine. I have another concern here since I see that behavior of this method is changed here.
defined? returns a string describing its argument by http://ruby-doc.org/core-2.5.0/doc/keywords_rdoc.html
Example:
@compressed = nil # or @compressed = false
defined? @compressed # => "instance-variable"That means if we used @compressed like nil or false, we would need to change this method to the previous version in order to get right behavior.
I would recomend to initialize @compressed in the controller in order to get rid of using defined? there.
if it is a good reason, I'll send a patch?
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index d769e2c..317723b 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -719,6 +719,7 @@ def initialize(value, compress: true, compress_threshold: DEFAULT_COMPRESS_LIMIT
@version = version
@created_at = Time.now.to_f
@expires_in = expires_in && expires_in.to_f
+ @compressed = nil
compress!(compress_threshold) if compress
end
@@ -798,7 +799,7 @@ def compress!(compress_threshold)
end
def compressed?
- defined?(@compressed)
+ @compressed
end
def uncompress(value)There was a problem hiding this comment.
The current implementation is fine. A string is a truthy value and that is only want we need. @compressed will never be @nil or @false because we serialize this instance using marshal and we don't want to always serialize @compressed and your implementation always serialize @compressed.
There was a problem hiding this comment.
You are right. Sorry for distraction.
There was a problem hiding this comment.
No problem! thank you for double checking the implementation.
|
PLEASE cut a 5.2.1 release with this fix. It is blocking our upgrade path to Rails 5.2.x... |
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_valueinstance 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.
Fix incoming.