Class: Benedictus::Heuristics::ExpensivePerRowScan

Inherits:
Base
  • Object
show all
Defined in:
lib/benedictus/heuristics/expensive_per_row_scan.rb

Overview

Flags scan nodes whose per-row cost is anomalously high — usually a sign that correlated subplans (or expensive filter functions) are multiplying the work the scan does for every outer row.

A normal Seq Scan tends to cost ~0.05 per row; anything above ~1.0 is almost always doing something extra (a SubPlan running per row, a SECURITY DEFINER UDF in a filter, etc.).

Constant Summary collapse

DEFAULT_THRESHOLD =
1.0
SCAN_TYPES =
["Seq Scan", "Index Scan", "Index Only Scan", "Bitmap Heap Scan"].freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

apply

Constructor Details

#initialize(threshold: DEFAULT_THRESHOLD) ⇒ ExpensivePerRowScan

Returns a new instance of ExpensivePerRowScan.



20
21
22
23
# File 'lib/benedictus/heuristics/expensive_per_row_scan.rb', line 20

def initialize(threshold: DEFAULT_THRESHOLD)
  super()
  @threshold = threshold
end

Class Method Details

.config_keyObject



16
17
18
# File 'lib/benedictus/heuristics/expensive_per_row_scan.rb', line 16

def self.config_key
  :per_row_cost_threshold
end

Instance Method Details

#apply(node) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/benedictus/heuristics/expensive_per_row_scan.rb', line 25

def apply(node)
  return [] unless SCAN_TYPES.include?(node.node_type)
  return [] unless node.plan_rows&.positive?
  return [] unless node.total_cost && node.startup_cost

  per_row = (node.total_cost - node.startup_cost) / node.plan_rows.to_f
  return [] if per_row < @threshold

  subplans = subplan_children(node)

  [
    warning(
      severity: subplans.any? ? :critical : :warning,
      code: :expensive_per_row_scan,
      message: build_message(node, per_row, subplans),
      suggestion: build_suggestion(subplans)
    )
  ]
end