aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXavier Noria <fxn@hashref.com>2011-12-03 14:26:34 +0100
committerXavier Noria <fxn@hashref.com>2011-12-03 14:26:34 +0100
commitde24ed9f2d582096fcc5b83984f187c2a094e83a (patch)
tree80c5b1ab5dccbd10b73e4474181c85704b014c47
parenta02691ce09c327e307a02f757c89d3cbd4d5ee56 (diff)
downloadrails-de24ed9f2d582096fcc5b83984f187c2a094e83a.tar.gz
rails-de24ed9f2d582096fcc5b83984f187c2a094e83a.tar.bz2
rails-de24ed9f2d582096fcc5b83984f187c2a094e83a.zip
removes the convenience instance version of AR::Base.silence_auto_explain
Rationale: As discussed with José and Jon, this convenience shortcut is not clearly justified and it could let the user thing the disabled EXPLAINs are related to the model instance rather than being globally disabled.
-rw-r--r--activerecord/CHANGELOG.md4
-rw-r--r--activerecord/lib/active_record/base.rb2
-rw-r--r--activerecord/lib/active_record/explain.rb166
-rw-r--r--activerecord/test/cases/explain_test.rb7
-rw-r--r--railties/guides/source/active_record_querying.textile5
5 files changed, 85 insertions, 99 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index f483fdd844..888bc43ec9 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,7 +1,7 @@
## Rails 3.2.0 (unreleased) ##
-* Implements `silence_auto_explain`. This method allows the user to selectively disable
- automatic EXPLAINs within a block. *fxn*
+* Implements `AR::Base.silence_auto_explain`. This method allows the user to
+ selectively disable automatic EXPLAINs within a block. *fxn*
* Implements automatic EXPLAIN logging for slow queries.
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index dcd091aecb..3d55729318 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -2213,7 +2213,7 @@ MSG
include Associations, NamedScope
include IdentityMap
include ActiveModel::SecurePassword
- include Explain
+ extend Explain
# AutosaveAssociation needs to be included before Transactions, because we want
# #save_with_autosave_associations to be wrapped inside a transaction.
diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb
index b506108f21..abe6fff5d5 100644
--- a/activerecord/lib/active_record/explain.rb
+++ b/activerecord/lib/active_record/explain.rb
@@ -1,106 +1,96 @@
module ActiveRecord
module Explain
- extend ActiveSupport::Concern
+ # logging_query_plan calls could appear nested in the call stack. In
+ # particular this happens when a relation fetches its records, since
+ # that results in find_by_sql calls downwards.
+ #
+ # This flag allows nested calls to detect this situation and bypass
+ # it, thus preventing repeated EXPLAINs.
+ LOGGING_QUERY_PLAN = :logging_query_plan
- module ClassMethods
- # logging_query_plan calls could appear nested in the call stack. In
- # particular this happens when a relation fetches its records, since
- # that results in find_by_sql calls downwards.
- #
- # This flag allows nested calls to detect this situation and bypass
- # it, thus preventing repeated EXPLAINs.
- LOGGING_QUERY_PLAN = :logging_query_plan
-
- # 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.
- def logging_query_plan(&block) # :nodoc:
- threshold = auto_explain_threshold_in_seconds
- if threshold && !Thread.current[LOGGING_QUERY_PLAN] && !Thread.current[SILENCED]
- begin
- Thread.current[LOGGING_QUERY_PLAN] = true
- start = Time.now
- result, sqls, binds = collecting_sqls_for_explain(&block)
- logger.warn(exec_explain(sqls, binds)) if Time.now - start > threshold
- result
- ensure
- Thread.current[LOGGING_QUERY_PLAN] = false
- end
- else
- yield
+ # 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.
+ def logging_query_plan(&block) # :nodoc:
+ threshold = auto_explain_threshold_in_seconds
+ if threshold && !Thread.current[LOGGING_QUERY_PLAN] && !Thread.current[SILENCED]
+ begin
+ Thread.current[LOGGING_QUERY_PLAN] = true
+ start = Time.now
+ result, sqls, binds = collecting_sqls_for_explain(&block)
+ logger.warn(exec_explain(sqls, binds)) if Time.now - start > threshold
+ result
+ ensure
+ Thread.current[LOGGING_QUERY_PLAN] = false
end
+ else
+ yield
end
+ end
- # SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
- # our own EXPLAINs now matter how loopingly beautiful that would be.
- SKIP_EXPLAIN_FOR = %w(SCHEMA EXPLAIN)
- def ignore_explain_notification?(payload) # :nodoc:
- payload[:exception] || SKIP_EXPLAIN_FOR.include?(payload[:name])
- end
-
- # Collects all queries executed while the passed block runs. Returns an
- # array with three elements, the result of the block, the strings with the
- # queries, and their respective bindings.
- def collecting_sqls_for_explain # :nodoc:
- sqls = []
- binds = []
- callback = lambda do |*args|
- payload = args.last
- unless ignore_explain_notification?(payload)
- sqls << payload[:sql]
- binds << payload[:binds]
- end
- end
+ # SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
+ # our own EXPLAINs now matter how loopingly beautiful that would be.
+ SKIP_EXPLAIN_FOR = %w(SCHEMA EXPLAIN)
+ def ignore_explain_notification?(payload) # :nodoc:
+ payload[:exception] || SKIP_EXPLAIN_FOR.include?(payload[:name])
+ end
- result = nil
- ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
- result = yield
+ # Collects all queries executed while the passed block runs. Returns an
+ # array with three elements, the result of the block, the strings with the
+ # queries, and their respective bindings.
+ def collecting_sqls_for_explain # :nodoc:
+ sqls = []
+ binds = []
+ callback = lambda do |*args|
+ payload = args.last
+ unless ignore_explain_notification?(payload)
+ sqls << payload[:sql]
+ binds << payload[:binds]
end
-
- [result, sqls, binds]
end
- # Makes the adapter execute EXPLAIN for the given queries and bindings.
- # Returns a formatted string ready to be logged.
- def exec_explain(sqls, binds) # :nodoc:
- sqls.zip(binds).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")
+ result = nil
+ ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
+ result = yield
end
- SILENCED = :silence_explain
+ [result, sqls, binds]
+ end
- # 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.
- def silence_auto_explain
- # Implemented as a flag rather that setting the threshold to nil
- # because we should not depend on a value that may be changed
- # downwards.
- Thread.current[SILENCED] = true
- yield
- ensure
- Thread.current[SILENCED] = false
- end
+ # Makes the adapter execute EXPLAIN for the given queries and bindings.
+ # Returns a formatted string ready to be logged.
+ def exec_explain(sqls, binds) # :nodoc:
+ sqls.zip(binds).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
- # A convenience instance method that delegates to the class method of the
- # same name.
- def silence_auto_explain(&block)
- self.class.silence_auto_explain(&block)
+ SILENCED = :silence_explain
+
+ # 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.
+ def silence_auto_explain
+ # Implemented as a flag rather that setting the threshold to nil
+ # because we should not depend on a value that may be changed
+ # downwards.
+ Thread.current[SILENCED] = true
+ yield
+ ensure
+ Thread.current[SILENCED] = false
end
end
end
diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb
index ff460b44eb..0284cca920 100644
--- a/activerecord/test/cases/explain_test.rb
+++ b/activerecord/test/cases/explain_test.rb
@@ -83,11 +83,8 @@ if ActiveRecord::Base.connection.supports_explain?
def test_silence_auto_explain
base.expects(:collecting_sqls_for_explain).never
base.logger.expects(:warn).never
-
- [base, cars(:honda)].each do |target|
- target.silence_auto_explain do
- with_threshold(0) { Car.all }
- end
+ base.silence_auto_explain do
+ with_threshold(0) { Car.all }
end
end
diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile
index 742aefc32f..0cbabd71a1 100644
--- a/railties/guides/source/active_record_querying.textile
+++ b/railties/guides/source/active_record_querying.textile
@@ -1391,11 +1391,10 @@ production modes.
h5. Disabling Automatic EXPLAIN
-Automatic EXPLAIN can be selectively silenced with +silence_auto_explain+, which
-is a class and instance method of +ActiveRecord::Base+:
+Automatic EXPLAIN can be selectively silenced with +ActiveRecord::Base.silence_auto_explain+:
<ruby>
-silence_auto_explain do
+ActiveRecord::Base.silence_auto_explain do
# no automatic EXPLAIN is triggered here
end
</ruby>