Don't cast `stale_state` to String

I'm looking at reducing the allocations and memory footprint
of Active Record models, and based on a small benchmark I saw
a lot of numeric strings being created from `stale_state`.

I tracked this cast all the way down to 1c07b84df9
but unfortunately the commit message doesn't explain why it was added.

I can't think of a reason why this would be needed.

The benchmark is essentially just: `Post.includes(:comments).to_a` with
`100` posts and `20` comments each.

```
Total allocated: 4.69 MB (45077 objects)
Total retained: 3.84 MB (29623 objects)

retained objects by location
-----------------------------------
...
      2000  activerecord/lib/active_record/associations/belongs_to_association.rb:152

retained memory by location
-----------------------------------
...
  80.00 kB  activerecord/lib/active_record/associations/belongs_to_association.rb:152
```

NB: This break the final assertion of the Rails 6.1 Marshal backward compatibility
test, but I think it's an acceptable tradeoff.
This commit is contained in:
Jean Boussier 2024-05-02 14:36:34 +02:00
parent 2c79c87a19
commit 2cadcb2718
4 changed files with 5 additions and 6 deletions

View File

@ -148,8 +148,7 @@ module ActiveRecord
end
def stale_state
result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
result && result.to_s
owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
end
end
end

View File

@ -41,8 +41,9 @@ module ActiveRecord
end
def stale_state
foreign_key = super
foreign_key && [foreign_key.to_s, owner[reflection.foreign_type].to_s]
if foreign_key = super
[foreign_key, owner[reflection.foreign_type]]
end
end
end
end

View File

@ -82,7 +82,7 @@ module ActiveRecord
def stale_state
if through_reflection.belongs_to?
Array(through_reflection.foreign_key).filter_map do |foreign_key_column|
owner[foreign_key_column] && owner[foreign_key_column].to_s
owner[foreign_key_column]
end.presence
end
end

View File

@ -33,7 +33,6 @@ class MarshalSerializationTest < ActiveRecord::TestCase
assert_equal "Have a nice day", topic.content
assert_predicate topic.association(:replies), :loaded?
assert_predicate topic.replies.first.association(:topic), :loaded?
assert_same topic, topic.replies.first.topic
end
def test_deserializing_rails_7_1_marshal_basic