Skip to content
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

5-0-stable: cache_key respects the limit in a relation even if a relation is not loaded #28879

Merged
merged 1 commit into from
Apr 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 16 additions & 6 deletions activerecord/lib/active_record/collection_cache_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,27 @@ def collection_cache_key(collection = all, timestamp_column = :updated_at) # :no
if collection.loaded?
size = collection.size
if size > 0
timestamp = collection.max_by(&timestamp_column).public_send(timestamp_column)
timestamp = collection.max_by(&timestamp_column)._read_attribute(timestamp_column)
end
else
column_type = type_for_attribute(timestamp_column.to_s)
column = "#{connection.quote_table_name(collection.table_name)}.#{connection.quote_column_name(timestamp_column)}"
select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"

query = collection
.unscope(:select)
.select("COUNT(*) AS #{connection.quote_column_name("size")}", "MAX(#{column}) AS timestamp")
.unscope(:order)
result = connection.select_one(query)
if collection.limit_value || collection.offset_value
query = collection.spawn
query.select_values = [column]
subquery_alias = "subquery_for_cache_key"
subquery_column = "#{subquery_alias}.#{timestamp_column}"
subquery = query.arel.as(subquery_alias)
arel = Arel::SelectManager.new(query.engine).project(select_values % subquery_column).from(subquery)
else
query = collection.unscope(:order)
query.select_values = [select_values % column]
arel = query.arel
end

result = connection.select_one(arel, nil, query.bound_attributes)

if result.blank?
size = 0
Expand Down
30 changes: 28 additions & 2 deletions activerecord/test/cases/collection_cache_key_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,34 @@ class CollectionCacheKeyTest < ActiveRecord::TestCase
end

test "cache_key for relation" do
developers = Developer.where(name: "David")
last_developer_timestamp = developers.order(updated_at: :desc).first.updated_at
developers = Developer.where(salary: 100000).order(updated_at: :desc)
last_developer_timestamp = developers.first.updated_at

assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/, developers.cache_key)

/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/ =~ developers.cache_key

assert_equal Digest::MD5.hexdigest(developers.to_sql), $1
assert_equal developers.count.to_s, $2
assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $3
end

test "cache_key for relation with limit" do
developers = Developer.where(salary: 100000).order(updated_at: :desc).limit(5)
last_developer_timestamp = developers.first.updated_at

assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/, developers.cache_key)

/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/ =~ developers.cache_key

assert_equal Digest::MD5.hexdigest(developers.to_sql), $1
assert_equal developers.count.to_s, $2
assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $3
end

test "cache_key for loaded relation" do
developers = Developer.where(salary: 100000).order(updated_at: :desc).limit(5).load
last_developer_timestamp = developers.first.updated_at

assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/, developers.cache_key)

Expand Down