

ActiveRecord::Core "#find" now reuses "#find_by" cache key
source link: https://blog.saeloun.com/2022/02/09/rails-prevent-duplicates-in-find_by-cache
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

When querying using #find
or #find_by
results are stored to cache.
This helps Rails load some queries from the cache instead of overloading the database server.
Each query is responsible for generating a cache key and storing the result in the cache.
This causes some irregularities to appear.
Before
One small oversight was #find
and #find_by(id: ...)
using different cache keys.
Both queries return the same result but do not store the result to the exact cache location.
Let’s look into how ActiveRecord Core works:
def find(*ids) # :nodoc:
# We don't have cache keys for this stuff yet
return super unless ids.length == 1
return super if block_given? ||
primary_key.nil? ||
scope_attributes? ||
columns_hash.key?(inheritance_column) && !base_class?
id = ids.first
return super if StatementCache.unsupported_value?(id)
key = primary_key
statement = cached_find_by_statement(key) { |params|
where(key => params.bind).limit(1)
}
record = statement.execute([id], connection)&.first
unless record
raise RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id)
end
record
end
We can see here that the cache key is just for primary_key
(which in most scenarios is "id"
).
Let’s go through the #find_by
method that accepts a hash of attributes.
def find_by(*args) # :nodoc:
return super if scope_attributes? || reflect_on_all_aggregations.any? ||
columns_hash.key?(inheritance_column) && !base_class?
hash = args.first
return super if !(Hash === hash) || hash.values.any? { |v|
StatementCache.unsupported_value?(v)
}
return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) }
keys = hash.keys
statement = cached_find_by_statement(keys) { |params|
wheres = keys.each_with_object({}) { |param, o|
o[param] = params.bind
}
where(wheres).limit(1)
}
begin
statement.execute(hash.values, connection)&.first
rescue TypeError
raise ActiveRecord::StatementInvalid
end
end
The cache key here gets set to hash.keys
which returns an
array of the columns that find_by
searches with.
Which is where the ambiguity arises.
While #find
returns the cache key "id"
,
find_by
returns the cache key ["id"]
.
After
Rails ActiveRecord::Core “#find” now reuses “#find_by” cache key. Both queries use the same cache location.
Query Cache Key find(123) [“id”] find_by(id: 123) [“id”] find_by(id: 123, foo: true) [“id”, “foo”]
It was a simple fix added to the #find
method,
which now pushes primary_key
to an array.
def find(*ids) # :nodoc:
# We don't have cache keys for this stuff yet
return super unless ids.length == 1
return super if block_given? || primary_key.nil? || scope_attributes?
id = ids.first
return super if StatementCache.unsupported_value?(id)
cached_find_by([primary_key], [id]) ||
raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}", name, primary_key, id))
end
Minor tweaks to core libraries can lead to huge benefits across applications!
Recommend
-
186
文章分四个部分介绍了 ActiveRecord 中的重要内容,模型的创建过程、Scope 和查询的实现、模型关系的实现以及最后的 Migrations 任务的实现和执行过程,各个模块之间没有太多的关联,由于文章内容比较多,如果读者只对某一部分的内容感兴趣,可以只挑选一部分进行...
-
11
A scary side of ActiveRecord's findHi, weʼre arkency 👋
-
8
Contributor kivikakk commented...
-
9
Rails 6.1 adds ActiveRecord methods `#sole` and `#find_sole_by` Mar 16, 2021 , by Swaathi Kakarla 1 minute read Inspired by Django’s
-
7
Need more help? Contact support ...
-
15
NoMethodError - undefined method 'find_by' for []: ActiveRecord :: Relation advertisements I've been following Michael Heartl tu...
-
8
Rails 7 now lazy loads schema cache Apr 20, 2022 , by Swaathi Kakarla 1 minute read Base operations...
-
10
Using Redis Cache for session data storage in ASP.NET Core Posted on: 11-12-2017
-
6
Amazon File Cache Now Generally Available Oct 08, 2022 2 min...
-
9
Need more help? Contact support...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK