Module: ActiveRecord::Explain

Included in:
Base, Relation
Defined in:
activerecord/lib/active_record/explain.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(base) ⇒ Object


5
6
7
8
9
10
11
12
# File 'activerecord/lib/active_record/explain.rb', line 5

def self.extended(base)
  base.class_eval do
    # If a query takes longer than these many seconds we log its query plan
    # automatically. nil disables this feature.
    class_attribute :auto_explain_threshold_in_seconds, :instance_writer => false
    self.auto_explain_threshold_in_seconds = nil
  end
end

Instance Method Details

#collecting_queries_for_explainObject

Relation#explain needs to be able to collect the queries regardless of whether auto explain is enabled. This method serves that purpose.


46
47
48
49
50
51
52
53
# File 'activerecord/lib/active_record/explain.rb', line 46

def collecting_queries_for_explain # :nodoc:
  current = Thread.current
  original, current[:available_queries_for_explain] = current[:available_queries_for_explain], []
  return yield, current[:available_queries_for_explain]
ensure
  # Note that the return value above does not depend on this assigment.
  current[:available_queries_for_explain] = original
end

#exec_explain(queries) ⇒ Object

Makes the adapter execute EXPLAIN for the tuples of queries and bindings. Returns a formatted string ready to be logged.


57
58
59
60
61
62
63
64
65
66
67
68
# File 'activerecord/lib/active_record/explain.rb', line 57

def exec_explain(queries) # :nodoc:
  queries && queries.map do |sql, bind|
    [].tap do |msg|
      msg << "EXPLAIN for: #{sql}"
      unless bind.empty?
        bind_msg = bind.map {|col, val| [col.name, val]}.inspect
        msg.last << " #{bind_msg}"
      end
      msg << connection.explain(sql, bind)
    end.join("\n")
  end.join("\n")
end

#logging_query_planObject

If auto explain is enabled, this method triggers EXPLAIN logging for the queries triggered by the block if it takes more than the threshold as a whole. That is, the threshold is not checked against each individual query, but against the duration of the entire block. This approach is convenient for relations.

The available_queries_for_explain thread variable collects the queries to be explained. If the value is nil, it means queries are not being currently collected. A false value indicates collecting is turned off. Otherwise it is an array of queries.


24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'activerecord/lib/active_record/explain.rb', line 24

def logging_query_plan # :nodoc:
  return yield unless logger

  threshold = auto_explain_threshold_in_seconds
  current   = Thread.current
  if threshold && current[:available_queries_for_explain].nil?
    begin
      queries = current[:available_queries_for_explain] = []
      start = Time.now
      result = yield
      logger.warn(exec_explain(queries)) if Time.now - start > threshold
      result
    ensure
      current[:available_queries_for_explain] = nil
    end
  else
    yield
  end
end

#silence_auto_explainObject

Silences automatic EXPLAIN logging for the duration of the block.

This has high priority, no EXPLAINs will be run even if downwards the threshold is set to 0.

As the name of the method suggests this only applies to automatic EXPLAINs, manual calls to ActiveRecord::Relation#explain run.


77
78
79
80
81
82
83
# File 'activerecord/lib/active_record/explain.rb', line 77

def silence_auto_explain
  current = Thread.current
  original, current[:available_queries_for_explain] = current[:available_queries_for_explain], false
  yield
ensure
  current[:available_queries_for_explain] = original
end