[ActiveSupport::Cache, ActiveSupport#delegate_missing_to] instance variables "leaking" externally when caching array of ActiveRecord objects with attachments

I’ve found some buggy behavior when caching & uncaching an array of ActiveRecord objects with ActiveStorage attachments. Here’s a truncated code snippet demonstrating the bug:

class User < ActiveRecord::Base

has_one_attached :avatar


users = User.first 5

users.each { |user| user.avatar }

Rails.cache.write “key”, users

uncached_users = Rails.cache.read “key”


=> :@attachment_changes


=> {}

In the above code, every element of the array users is an instance of User. After writing to and reading from the cache, every element of uncached_users should still be an instance of User. However, after reading from the cache, the second and third elements of uncached_users are a symbol and empty hash. Here’s a full executable test case: https://gist.github.com/alipman88/12928dfd2f86afacd1af51aeb8ae5194

After some digging, I’ve determined the underlying cause is a bug in Ruby itself, allowing instance variables to be added or removed during marshallization. (Many of Rails’ cache stores use Marshal.load & Marshal.dump to serialize & deserialize objects.) While I’ll skip over the specifics, the bug will be corrected in future implementations of Ruby. Here’s the ticket on Ruby’s issue tracker, for anyone seeking more detail: https://bugs.ruby-lang.org/issues/15968

Although future versions of Ruby will fix the underlying issue by raising a RuntimeError, this won’t completely solve the problem: Rails developers may still encounter a somewhat confounding error without an immediately obvious cause.

I’ve submitted a pull request that fixes this issue by patching the delegate_missing_to extension through which this behavior arises: https://github.com/rails/rails/pull/36623

It’s been a couple weeks since I submitted my pull request. Per advice in the Contributing to Ruby on Rails guide, I’m hoping to nudge things along and attract some code reviewers by posting here. I respect that Rails is a volunteer project, and appreciate any feedback when folks are able to provide it.