Class: Scryer::PerformanceRules::UnboundedTableScanRule
- Defined in:
- lib/scryer/performance_rules/unbounded_table_scan_rule.rb
Overview
Flags Model.all.each/Model.where(...).each (or .order(...).each) —
chaining .each directly onto a query loads every matching row into
memory at once before iterating. find_each/find_in_batches load and
yield records in bounded batches instead, keeping memory flat regardless
of table size. This only looks at the literal chain (Const.query.each)
so it won't catch the same problem one step removed — e.g. a variable
assigned from the query and iterated later (that pattern is out of scope
here; see NPlusOneQueryRule, which does track simple local
assignments, for a related check on what happens inside such a loop).
Constant Summary collapse
- QUERY_METHODS =
%w[all where order].freeze
Instance Attribute Summary
Attributes inherited from Rule
Instance Method Summary collapse
Methods inherited from Rule
Constructor Details
This class inherits a constructor from Scryer::Rule
Instance Method Details
#scan ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/scryer/performance_rules/unbounded_table_scan_rule.rb', line 20 def scan findings = [] Ast.each_node(sexp) do |node| next unless Ast.tagged?(node, :method_add_block) call_node = node[1] receiver, method_name = Ast.call_name(call_node) || [nil, nil] next unless method_name == "each" next unless receiver next unless direct_query_chain?(receiver) line = Ast.line_of(call_node) findings << finding( line: line, message: "`.each` is chained directly onto a query — Active Record loads every " \ "matching row into memory before the block runs even once, which can exhaust " \ "memory (or just be very slow) once the table is large.", suggested_fix: "Use `find_each` (row-by-row, fixed batch size) or `find_in_batches` " \ "(access a batch `Array` at a time) instead of `.each`, e.g. " \ "`Model.where(...).find_each { |record| ... }` — Active Record loads and " \ "discards records in bounded batches instead of all at once." ) end findings end |