New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was it changed intentionally since defined?(@compressed) ? @compressed : false
looks correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. Sorry for distraction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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_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.
Fix incoming.