Today I stumbled across a pretty harmless-looking query in our application which turned out to be pretty harmful and caused huge memory usage as well as downing our passenger workers by letting requests take up to 60 seconds. We had a method that received a scope and then checked, if the scope parameter was blank?
and aborted the method execution in this case.
def foo(scope)
return if scope.blank?
# Use scope, e.g.
scope.find(...)
end
We then called this method with an all
scope: foo(Media::Document::Base.all)
. Be careful with this. all
returns a scope (ActiveRecord::Relation
) which looks and is harmless at first because nothing is loaded into memory at that point. However, calling .all.blank?
loads all records into memory and then evaluates, if the scope is blank.
What you are searching for is a normal nil
check,
ActiveRecord::FinderMethods.exists?
Show archive.org snapshot
or its counterpart empty?
.
def foo(scope)
return unless scope
# Use scope, e.g.
scope.find(...)
end
def foo(scope)
return unless scope&.exists?
# Use scope, e.g.
scope.find(...)
end
This will produce an additional query in the database, but is fast and does not load any records into memory.