Module: ActiveRecord::SqlAnalyzer::Monkeypatches::Query

Defined in:
lib/active_record/sql_analyzer/monkeypatches/query.rb

Defined Under Namespace

Classes: QueryAnalyzerCall

Instance Method Summary collapse

Instance Method Details

#_query_analyzer_private_drain_transaction_queueObject

Drain the transaction query queue. Log the current transaction out to any logger that samples it.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/active_record/sql_analyzer/monkeypatches/query.rb', line 36

def _query_analyzer_private_drain_transaction_queue
  reencoded_calls = nil

  SqlAnalyzer.config[:analyzers].each do |analyzer|
    if SqlAnalyzer.config[:should_log_sample_proc].call(analyzer[:name])
      # Again, trying to only re-encode and join strings if the transaction is actually
      # sampled.
      reencoded_calls ||= @_query_analyzer_private_transaction_queue.map do |call|
        QueryAnalyzerCall.new(call.sql.encode(Encoding::UTF_8, invalid: :replace, undef: :replace), call.caller)
      end

      has_matching_calls = reencoded_calls.any? { |call| call.sql =~ analyzer[:table_regex] }

      if has_matching_calls
        matching_calls = reencoded_calls.select do |call|
          (call.sql =~ /^(BEGIN|COMMIT|ROLLBACK|UPDATE|INSERT|DELETE)/) || (call.sql =~ analyzer[:table_regex])
        end

        SqlAnalyzer.background_processor <<
          _query_analyzer_private_query_stanza(matching_calls, analyzer)
      end
    end
  end
end

#_query_analyzer_private_query_stanza(calls, analyzer) ⇒ Object

Helper method to construct the event for a query or transaction. safe_sql [string]: SQL statement or combined SQL statement for transaction calls: A list of QueryAnalyzerCall objects to be turned into call hashes



64
65
66
67
68
69
70
71
72
# File 'lib/active_record/sql_analyzer/monkeypatches/query.rb', line 64

def _query_analyzer_private_query_stanza(calls, analyzer)
  {
    # Calls are of the format {sql: String, caller: String}
    calls: calls.map(&:to_h),
    logger: analyzer[:logger_instance],
    tag: Thread.current[:_ar_analyzer_tag],
    request_path: Thread.current[:_ar_analyzer_request_path],
  }
end

#execute(sql, *args) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/active_record/sql_analyzer/monkeypatches/query.rb', line 9

def execute(sql, *args)
  return super unless SqlAnalyzer.config

  if @_query_analyzer_private_transaction_queue
    @_query_analyzer_private_transaction_queue << QueryAnalyzerCall.new(sql, caller)
  else
    safe_sql = nil

    SqlAnalyzer.config[:analyzers].each do |analyzer|
      if SqlAnalyzer.config[:should_log_sample_proc].call(analyzer[:name])
        # This is here rather than above intentionally.
        # We assume we're not going to be analyzing 100% of queries and want to only re-encode
        # when it's actually relevant.
        safe_sql ||= sql.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)

        if safe_sql =~ analyzer[:table_regex]
          SqlAnalyzer.background_processor <<
            _query_analyzer_private_query_stanza([QueryAnalyzerCall.new(safe_sql, caller)], analyzer)
        end
      end
    end
  end

  super
end

#transaction(requires_new: nil, isolation: nil, joinable: true) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/active_record/sql_analyzer/monkeypatches/query.rb', line 74

def transaction(requires_new: nil, isolation: nil, joinable: true)
  must_clear_transaction = false

  if SqlAnalyzer.config[:consolidate_transactions]
    if @_query_analyzer_private_transaction_queue.nil?
      must_clear_transaction = true
      @_query_analyzer_private_transaction_queue = []
    end
  end

  super
ensure
  if must_clear_transaction
    _query_analyzer_private_drain_transaction_queue
    @_query_analyzer_private_transaction_queue = nil
  end
end