Module: Sixty::Instrument::ActiveRecord

Defined in:
lib/sixty/instrument/active_record.rb

Overview

ActiveRecord.

What is captured beyond timing is the point of the product:

rows — how many the statement returned. The 30 -> 30,000 signal, and the
     one an ORM makes easy to lose: `.where(...)` without a limit is
     one character away from `.all`, and on a development database
     both take four milliseconds.
calls — every query, credited to every enclosing method and to the
     request. One query becoming twelve is an N+1 being born.

── Why a notification subscriber and not a patched adapter ───────────────

sql.active_record is a public, stable interface that every adapter emits and every Rails version has. Patching exec_query would work on one adapter and one version and break quietly on the next, and "quietly" is the failure mode this project refuses. The cost is that the event arrives after the query — so the span is assembled from the event's own start and finish times rather than measured around it, which is exactly what Tracer.record exists for.

Constant Summary collapse

IGNORED_NAMES =

Statements Rails issues about itself rather than on the application's behalf. A schema load at boot is not an operation anyone can act on, and a cached query never reached the database at all — recording it would report database work that did not happen.

['SCHEMA', 'TRANSACTION', 'EXPLAIN'].freeze

Class Method Summary collapse

Class Method Details

.dialectObject

Which SQL dialect this application speaks.

It decides whether "alice@example.com" is an identifier to keep or a string literal to strip, so guessing from the text is not an option — a wrong guess in one direction transmits a customer's data. Asked of the adapter, which knows, and cached because the answer cannot change for the life of a process.

connection_db_config rather than a live connection: reading this must never be a reason to open one.



64
65
66
67
68
69
70
71
72
73
# File 'lib/sixty/instrument/active_record.rb', line 64

def dialect
  return @dialect if defined?(@dialect) && @dialect

  @dialect = begin
    name = ::ActiveRecord::Base.connection_db_config.adapter.to_s
    name.match?(/mysql|trilogy|maria/i) ? :mysql : :postgres
  rescue StandardError
    :postgres
  end
end

.install(config) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/sixty/instrument/active_record.rb', line 37

def install(config)
  return false if @installed
  return false unless defined?(::ActiveSupport::Notifications)

  @config = config
  @subscriber = ::ActiveSupport::Notifications.monotonic_subscribe('sql.active_record') do |_name, started, finished, _id, payload|
    record(payload, (finished - started) * 1000.0)
  end

  Sixty.before_flush { capture_plans } if config.capture_plans
  @installed = true
end

.installed?Boolean

Returns:

  • (Boolean)


50
51
52
# File 'lib/sixty/instrument/active_record.rb', line 50

def installed?
  @installed == true
end