awesome_explain 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/workflows/mongodb.yml +53 -0
- data/.github/workflows/postgres.yml +56 -0
- data/.gitignore +11 -0
- data/Appraisals +11 -0
- data/Gemfile.lock +209 -49
- data/LICENSE.txt +4 -20
- data/README.md +155 -7
- data/Rakefile +35 -1
- data/app/models/awesome_explain/application_record.rb +5 -0
- data/app/models/awesome_explain/controller.rb +20 -0
- data/app/models/awesome_explain/delayed_job.rb +7 -0
- data/app/models/awesome_explain/explain.rb +23 -0
- data/app/models/awesome_explain/log.rb +7 -0
- data/app/models/awesome_explain/pg_dml_stat.rb +4 -0
- data/app/models/awesome_explain/pg_seq_scan.rb +4 -0
- data/app/models/awesome_explain/plan_node.rb +52 -0
- data/app/models/awesome_explain/plan_tree.rb +66 -0
- data/app/models/awesome_explain/sidekiq_worker.rb +7 -0
- data/app/models/awesome_explain/sql_explain.rb +14 -0
- data/app/models/awesome_explain/sql_plan_node.rb +73 -0
- data/app/models/awesome_explain/sql_plan_stats.rb +34 -0
- data/app/models/awesome_explain/sql_plan_tree.rb +133 -0
- data/app/models/awesome_explain/sql_query.rb +7 -0
- data/app/models/awesome_explain/stacktrace.rb +11 -0
- data/awesome_explain.gemspec +16 -5
- data/bin/rails +14 -0
- data/data/mongodb/customers.bson +0 -0
- data/data/mongodb/customers.metadata.json +1 -0
- data/data/mongodb/line_items.bson +0 -0
- data/data/mongodb/line_items.metadata.json +1 -0
- data/data/mongodb/orders.bson +0 -0
- data/data/mongodb/orders.metadata.json +1 -0
- data/data/mongodb/products.bson +0 -0
- data/data/mongodb/products.metadata.json +1 -0
- data/data/postgresql/dvdrental.tar +0 -0
- data/db/migrate/20200507214801_stacktraces.rb +12 -0
- data/db/migrate/20200507214949_controllers.rb +16 -0
- data/db/migrate/20200507215205_logs.rb +22 -0
- data/db/migrate/20200507215243_explains.rb +27 -0
- data/gemfiles/rails_4.gemfile +7 -0
- data/gemfiles/rails_4.gemfile.lock +208 -0
- data/gemfiles/rails_5.gemfile +7 -0
- data/gemfiles/rails_5.gemfile.lock +209 -0
- data/gemfiles/rails_6.gemfile +7 -0
- data/gemfiles/rails_6.gemfile.lock +233 -0
- data/images/universe.png +0 -0
- data/lib/awesome_explain.rb +79 -2
- data/lib/awesome_explain/config.rb +196 -0
- data/lib/awesome_explain/engine.rb +5 -0
- data/lib/awesome_explain/insights/active_record_insights.rb +137 -0
- data/lib/awesome_explain/insights/base.rb +18 -0
- data/lib/awesome_explain/insights/mongoid_insights.rb +44 -0
- data/lib/awesome_explain/insights/sql_plans_insights.rb +64 -0
- data/lib/awesome_explain/kernel.rb +17 -0
- data/lib/awesome_explain/mongodb/base.rb +4 -0
- data/lib/awesome_explain/mongodb/command_start.rb +84 -0
- data/lib/awesome_explain/mongodb/command_success.rb +58 -0
- data/lib/awesome_explain/mongodb/formatter.rb +62 -0
- data/lib/awesome_explain/mongodb/helpers.rb +71 -0
- data/lib/awesome_explain/queue/command.rb +17 -0
- data/lib/awesome_explain/queue/simple_queue.rb +88 -0
- data/lib/awesome_explain/renderers/active_record.rb +114 -0
- data/lib/awesome_explain/renderers/base.rb +2 -0
- data/lib/awesome_explain/renderers/mongoid.rb +20 -33
- data/lib/awesome_explain/sidekiq_middleware.rb +17 -0
- data/lib/awesome_explain/stats/postgresql.rb +54 -0
- data/lib/awesome_explain/subscribers/active_record_passive_subscriber.rb +82 -0
- data/lib/awesome_explain/subscribers/active_record_subscriber.rb +187 -0
- data/lib/awesome_explain/subscribers/base.rb +3 -0
- data/lib/awesome_explain/subscribers/command_subscriber.rb +53 -0
- data/lib/awesome_explain/tasks/db.rb +325 -0
- data/lib/awesome_explain/utils/color.rb +16 -0
- data/lib/awesome_explain/version.rb +1 -1
- data/lib/tasks/ae.rake +28 -0
- data/lib/tasks/awesome_explain_tasks.rake +4 -0
- metadata +242 -25
- data/.travis.yml +0 -19
| @@ -0,0 +1,44 @@ | |
| 1 | 
            +
            module AwesomeExplain::Insights
         | 
| 2 | 
            +
              class MongoidInsights
         | 
| 3 | 
            +
                attr_accessor :command_subscriber, :metrics
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def self.analyze(metrics = [], &block)
         | 
| 6 | 
            +
                  instance = new
         | 
| 7 | 
            +
                  instance.init
         | 
| 8 | 
            +
                  instance.metrics = metrics
         | 
| 9 | 
            +
                  block_result = instance.instance_eval(&block)
         | 
| 10 | 
            +
                  instance.tear_down
         | 
| 11 | 
            +
                  block_result unless metrics.size.positive?
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def init
         | 
| 15 | 
            +
                  command_subscribers = Mongoid.default_client.send(:monitoring).subscribers['Command'] || Mongo::Monitoring::Global.subscribers['Command']
         | 
| 16 | 
            +
                  @command_subscriber = command_subscribers.select do |s|
         | 
| 17 | 
            +
                    s.class == AwesomeExplain::CommandSubscriber
         | 
| 18 | 
            +
                  end.first
         | 
| 19 | 
            +
                  @command_subscriber.clear
         | 
| 20 | 
            +
                  Thread.current['ae_analyze'] = true
         | 
| 21 | 
            +
                  Thread.current['ae_source'] = 'console'
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def tear_down
         | 
| 25 | 
            +
                  if @command_subscriber.nil?
         | 
| 26 | 
            +
                    puts 'Configure the command subscriber then try again.'
         | 
| 27 | 
            +
                    return
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  if @metrics.size.positive?
         | 
| 31 | 
            +
                    result = {}
         | 
| 32 | 
            +
                    @metrics.each do |m|
         | 
| 33 | 
            +
                      result[m] = @command_subscriber.get(m)
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    @command_subscriber.clear
         | 
| 37 | 
            +
                    return result
         | 
| 38 | 
            +
                  else
         | 
| 39 | 
            +
                    @command_subscriber.clear
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                  Thread.current['ae_analyze'] = false
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
            end
         | 
| @@ -0,0 +1,64 @@ | |
| 1 | 
            +
            module AwesomeExplain::Insights
         | 
| 2 | 
            +
              class SqlPlansInsights
         | 
| 3 | 
            +
                include Singleton
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                attr_accessor :plans_stats, :queries
         | 
| 6 | 
            +
                attr_reader :mutex
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def initialize
         | 
| 9 | 
            +
                  @plans_stats = []
         | 
| 10 | 
            +
                  @queries = []
         | 
| 11 | 
            +
                  @mutex = Mutex.new
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def add(plan_stats)
         | 
| 15 | 
            +
                  with_mutex { @plans_stats << plan_stats }
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def add_query(query)
         | 
| 19 | 
            +
                  with_mutex {
         | 
| 20 | 
            +
                    query = Niceql::Prettifier.prettify_sql query
         | 
| 21 | 
            +
                    @queries << query
         | 
| 22 | 
            +
                  }
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def plans_stats
         | 
| 26 | 
            +
                  with_mutex { @plans_stats }
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def queries
         | 
| 30 | 
            +
                  with_mutex { @queries }
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def clear
         | 
| 34 | 
            +
                  plans_stats.clear
         | 
| 35 | 
            +
                  queries.clear
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def self.add(plan_stats)
         | 
| 39 | 
            +
                  instance.add(plan_stats)
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def self.add_query(query)
         | 
| 43 | 
            +
                  instance.add_query(query)
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def self.plans_stats
         | 
| 47 | 
            +
                  instance.plans_stats
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                def self.queries
         | 
| 51 | 
            +
                  instance.queries
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                def self.clear
         | 
| 55 | 
            +
                  instance.clear
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                private
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def with_mutex
         | 
| 61 | 
            +
                  @mutex.synchronize { yield }
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
            end
         | 
| @@ -1,9 +1,21 @@ | |
| 1 1 | 
             
            module Kernel
         | 
| 2 2 | 
             
              def ae(query)
         | 
| 3 3 | 
             
                return AwesomeExplain::Renderers::Mongoid.new(query).print if mongoid_query?(query)
         | 
| 4 | 
            +
                return AwesomeExplain::Renderers::ActiveRecord.new(query).print if active_record_query?(query)
         | 
| 5 | 
            +
             | 
| 4 6 | 
             
                query
         | 
| 5 7 | 
             
              end
         | 
| 6 8 |  | 
| 9 | 
            +
              def analyze(&block)
         | 
| 10 | 
            +
                ::AwesomeExplain::MongoiddInsights.analyze(&block)
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              def analyze_ar(options = {}, &block)
         | 
| 14 | 
            +
                Thread.current['ae_analyze'] = true
         | 
| 15 | 
            +
                Thread.current['ae_source'] = 'console'
         | 
| 16 | 
            +
                ::AwesomeExplain::Insights::ActiveRecordInsights.analyze(options, &block)
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 7 19 | 
             
              private
         | 
| 8 20 |  | 
| 9 21 | 
             
              def mongoid_query?(query)
         | 
| @@ -11,4 +23,9 @@ module Kernel | |
| 11 23 | 
             
                  defined?(Mongoid::Criteria) &&
         | 
| 12 24 | 
             
                  (query.is_a?(Mongo::Collection::View::Aggregation) || query.is_a?(Mongoid::Criteria))
         | 
| 13 25 | 
             
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              def active_record_query?(query)
         | 
| 28 | 
            +
                defined?(ActiveRecord::Relation) &&
         | 
| 29 | 
            +
                  query.is_a?(ActiveRecord::Relation)
         | 
| 30 | 
            +
              end
         | 
| 14 31 | 
             
            end
         | 
| @@ -0,0 +1,84 @@ | |
| 1 | 
            +
            module AwesomeExplain::Mongodb
         | 
| 2 | 
            +
              module CommandStart
         | 
| 3 | 
            +
                extend ActiveSupport::Concern
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                included do
         | 
| 6 | 
            +
                  def handle_command_start(event)
         | 
| 7 | 
            +
                    command = event.command
         | 
| 8 | 
            +
                    command_name = event.command_name.to_sym
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    if db_explain_enabled?(command_name)
         | 
| 11 | 
            +
                      case AwesomeExplain::Config.instance.enabled?
         | 
| 12 | 
            +
                      when Rails.env.development?
         | 
| 13 | 
            +
                        handle_command_start_sync(event)
         | 
| 14 | 
            +
                      when Rails.env.staging?
         | 
| 15 | 
            +
                        handle_command_start_async(event)
         | 
| 16 | 
            +
                      end
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def process_command_start(event)
         | 
| 21 | 
            +
                    command = event.command
         | 
| 22 | 
            +
                    command_name = event.command_name.to_sym
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    begin
         | 
| 25 | 
            +
                      request_id = event.request_id
         | 
| 26 | 
            +
                      if command_name == :getMore
         | 
| 27 | 
            +
                        collection_name = event.command['collection']
         | 
| 28 | 
            +
                      else
         | 
| 29 | 
            +
                        collection_name = event.command[event.command_name]
         | 
| 30 | 
            +
                      end
         | 
| 31 | 
            +
                      @stats[:collections][collection_name] = Hash.new(0) if !@stats[:collections].include?(collection_name)
         | 
| 32 | 
            +
                      @stats[:collections][collection_name][command_name] += 1
         | 
| 33 | 
            +
                      @queries[request_id] = {
         | 
| 34 | 
            +
                        command_name: event.command_name,
         | 
| 35 | 
            +
                        command: command.include?('pipeline') ? command['pipeline'] : command.select {|k, v| COMMAND_ALLOWED_KEYS.include?(k)},
         | 
| 36 | 
            +
                        collection_name: collection_name,
         | 
| 37 | 
            +
                        stacktrace: caller,
         | 
| 38 | 
            +
                        lsid: command.dig('lsid', 'id').to_json
         | 
| 39 | 
            +
                      }.with_indifferent_access
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                      command = event.command
         | 
| 42 | 
            +
                      if command.include?('aggregate')
         | 
| 43 | 
            +
                        command = {
         | 
| 44 | 
            +
                          'aggregate': command['aggregate'],
         | 
| 45 | 
            +
                          'pipeline': command['pipeline'],
         | 
| 46 | 
            +
                          'cursor': command['cursor'],
         | 
| 47 | 
            +
                        }
         | 
| 48 | 
            +
                      end
         | 
| 49 | 
            +
                      r = ::AwesomeExplain::Renderers::Mongoid.new(nil, Mongoid.default_client.database.command({explain: command}).documents.first)
         | 
| 50 | 
            +
                      exp = AwesomeExplain::Explain.create({
         | 
| 51 | 
            +
                        collection: collection_name,
         | 
| 52 | 
            +
                        source_name: AwesomeExplain::Config.instance.app_name,
         | 
| 53 | 
            +
                        command: @queries[request_id][:command].to_json,
         | 
| 54 | 
            +
                        winning_plan: r.winning_plan_data.first,
         | 
| 55 | 
            +
                        winning_plan_raw: r.winning_plan.to_json,
         | 
| 56 | 
            +
                        used_indexes: r.winning_plan_data.last.join(', '),
         | 
| 57 | 
            +
                        duration: (r.execution_stats&.dig('executionTimeMillis').to_f/1000).round(5),
         | 
| 58 | 
            +
                        documents_returned: r.execution_stats&.dig('nReturned'),
         | 
| 59 | 
            +
                        documents_examined: r.execution_stats&.dig('totalDocsExamined'),
         | 
| 60 | 
            +
                        keys_examined: r.execution_stats&.dig('totalKeysExamined'),
         | 
| 61 | 
            +
                        rejected_plans: r.rejected_plans&.size,
         | 
| 62 | 
            +
                        session_id: Thread.current[:ae_session_id],
         | 
| 63 | 
            +
                        lsid: @queries[request_id][:lsid],
         | 
| 64 | 
            +
                        stacktrace_id: resolve_stracktrace_id(request_id),
         | 
| 65 | 
            +
                        controller_id: resolve_controller_id,
         | 
| 66 | 
            +
                      })
         | 
| 67 | 
            +
                      @queries[request_id][:explain_id] = exp&.id
         | 
| 68 | 
            +
                      @queries[request_id][:collscan] = exp&.collscan
         | 
| 69 | 
            +
                    rescue => exception
         | 
| 70 | 
            +
                      logger.warn exception.to_s
         | 
| 71 | 
            +
                      logger.warn exception.backtrace[0..5]
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  def handle_command_start_sync(event)
         | 
| 76 | 
            +
                    # process_command_start(event)
         | 
| 77 | 
            +
                    ::AwesomeExplain::Config.instance.queue << ::AwesomeExplain::Queue::Command.new(:process_command_start, event, self)
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  def handle_command_start_async(event)
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
            end
         | 
| @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            module AwesomeExplain::Mongodb
         | 
| 2 | 
            +
              module CommandSuccess
         | 
| 3 | 
            +
                extend ActiveSupport::Concern
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                included do
         | 
| 6 | 
            +
                  def handle_command_success(event)
         | 
| 7 | 
            +
                    if db_logging_enbled?
         | 
| 8 | 
            +
                      case AwesomeExplain::Config.instance.enabled?
         | 
| 9 | 
            +
                      when Rails.env.development?
         | 
| 10 | 
            +
                        handle_command_success_sync(event)
         | 
| 11 | 
            +
                      when Rails.env.staging?
         | 
| 12 | 
            +
                        handle_command_success_async(event)
         | 
| 13 | 
            +
                      end
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def process_command_success(event)
         | 
| 18 | 
            +
                    begin
         | 
| 19 | 
            +
                      command_name = event.command_name.to_sym
         | 
| 20 | 
            +
                      request_id = event.request_id
         | 
| 21 | 
            +
                      duration = event.duration.round(5)
         | 
| 22 | 
            +
                      @stats[:performed_queries][command_name] += 1
         | 
| 23 | 
            +
                      @stats[:total_duration] += duration
         | 
| 24 | 
            +
                      @queries[request_id][:duration] = duration
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                      log = {
         | 
| 27 | 
            +
                        operation: command_name,
         | 
| 28 | 
            +
                        app_name: AwesomeExplain::Config.instance.app_name,
         | 
| 29 | 
            +
                        source_name: resolve_source_name,
         | 
| 30 | 
            +
                        collscan: @queries[request_id][:collscan],
         | 
| 31 | 
            +
                        collection: @queries[request_id][:collection_name],
         | 
| 32 | 
            +
                        duration: duration,
         | 
| 33 | 
            +
                        command: @queries[request_id][:command].to_json,
         | 
| 34 | 
            +
                        session_id: Thread.current[:ae_session_id],
         | 
| 35 | 
            +
                        lsid: @queries[request_id][:lsid],
         | 
| 36 | 
            +
                        stacktrace_id: resolve_stracktrace_id(request_id),
         | 
| 37 | 
            +
                        explain_id: @queries[request_id][:explain_id],
         | 
| 38 | 
            +
                        controller_id: resolve_controller_id,
         | 
| 39 | 
            +
                        sidekiq_worker_id: resolve_sidekiq_class_id,
         | 
| 40 | 
            +
                      }
         | 
| 41 | 
            +
                      AwesomeExplain::Log.create(log)
         | 
| 42 | 
            +
                    rescue => exception
         | 
| 43 | 
            +
                      logger.warn exception.to_s
         | 
| 44 | 
            +
                      logger.warn exception.backtrace[0..5]
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def handle_command_success_sync(event)
         | 
| 49 | 
            +
                    # process_command_success(event)
         | 
| 50 | 
            +
                    ::AwesomeExplain::Config.instance.queue << ::AwesomeExplain::Queue::Command.new(:process_command_success, event, self)
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  def handle_command_success_async(event)
         | 
| 54 | 
            +
                    # TODO: How to handle using Queues
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
            end
         | 
| @@ -0,0 +1,62 @@ | |
| 1 | 
            +
            module AwesomeExplain::Mongodb
         | 
| 2 | 
            +
              module Formatter
         | 
| 3 | 
            +
                extend ActiveSupport::Concern
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                included do
         | 
| 6 | 
            +
                  def stats_table
         | 
| 7 | 
            +
                    table = Terminal::Table.new(title: 'Query Stats') do |t|
         | 
| 8 | 
            +
                      t << [
         | 
| 9 | 
            +
                        'Total Duration',
         | 
| 10 | 
            +
                        @stats[:total_duration] >= 1 ? "#{@stats[:total_duration]} seconds".purpleish : "#{@stats[:total_duration]} seconds".green
         | 
| 11 | 
            +
                      ]
         | 
| 12 | 
            +
                      t << :separator
         | 
| 13 | 
            +
                      t << [
         | 
| 14 | 
            +
                        total_performed_queries >= 100 ? "Performed Queries [#{total_performed_queries}]".purpleish : "Performed Queries [#{total_performed_queries}]".green,
         | 
| 15 | 
            +
                        formatted_performed_queries
         | 
| 16 | 
            +
                      ]
         | 
| 17 | 
            +
                      t << :separator
         | 
| 18 | 
            +
                      t << ['Collections Queried', formatted_collections]
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      sq = slowest_query
         | 
| 21 | 
            +
                      if sq[:duration] >= 0.5
         | 
| 22 | 
            +
                        t << :separator
         | 
| 23 | 
            +
                        t << ["Slowest Query [#{sq.dig(:duration)}]".purpleish, sq.dig(:command).inspect]
         | 
| 24 | 
            +
                      end
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                    puts table
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def slowest_query
         | 
| 30 | 
            +
                    @queries.sort_by {|id, data| data[:duration]}.last[1]
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  def total_performed_queries
         | 
| 34 | 
            +
                    @stats[:performed_queries].sum {|op, count| count}
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def formatted_performed_queries
         | 
| 38 | 
            +
                    find = @stats[:performed_queries][:find]
         | 
| 39 | 
            +
                    count = @stats[:performed_queries][:count]
         | 
| 40 | 
            +
                    distinct = @stats[:performed_queries][:distinct]
         | 
| 41 | 
            +
                    update = @stats[:performed_queries][:update]
         | 
| 42 | 
            +
                    insert = @stats[:performed_queries][:insert]
         | 
| 43 | 
            +
                    get_more = @stats[:performed_queries][:getMore]
         | 
| 44 | 
            +
                    delete = @stats[:performed_queries][:delete]
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    QUERIES.reject { |q| !@stats[:performed_queries][q].positive? }.sort_by {|q| @stats[:performed_queries][q]}.reverse.map do |q|
         | 
| 47 | 
            +
                      @stats[:performed_queries][q] >= 10 ? "#{q}: #{@stats[:performed_queries][q]}".purpleish : "#{q}: #{@stats[:performed_queries][q]}"
         | 
| 48 | 
            +
                    end.join(', ')
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  def formatted_collections
         | 
| 52 | 
            +
                    @stats[:collections].sort_by {|c, values| values.sum {|k, v| v}}.reverse.each.map do |data|
         | 
| 53 | 
            +
                      c = data[0]
         | 
| 54 | 
            +
                      sum = @stats[:collections][c].sum {|k, v| v}
         | 
| 55 | 
            +
                      cmds = @stats[:collections][c].map {|cmd, count| "#{cmd} [#{count}]"}.join(', ')
         | 
| 56 | 
            +
                      cmds = "#{c}: #{cmds}"
         | 
| 57 | 
            +
                      sum >= 10 ? cmds.purpleish : cmds
         | 
| 58 | 
            +
                    end.join("\n")
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
            end
         | 
| @@ -0,0 +1,71 @@ | |
| 1 | 
            +
            module AwesomeExplain::Mongodb
         | 
| 2 | 
            +
              module Helpers
         | 
| 3 | 
            +
                extend ActiveSupport::Concern
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                included do
         | 
| 6 | 
            +
                  def resolve_stracktrace_id(request_id)
         | 
| 7 | 
            +
                    stacktrace_str = @queries[request_id][:stacktrace]
         | 
| 8 | 
            +
                      .select {|c| c =~ /^#{Rails.root.to_s + '\/(lib|app|db)\/'}/ }
         | 
| 9 | 
            +
                      .map {|c| c.gsub Rails.root.to_s, ''}
         | 
| 10 | 
            +
                      .to_json
         | 
| 11 | 
            +
                    stacktrace = AwesomeExplain::Stacktrace.find_or_create_by({
         | 
| 12 | 
            +
                      stacktrace: stacktrace_str
         | 
| 13 | 
            +
                    })
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    stacktrace.id
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def resolve_controller_id
         | 
| 19 | 
            +
                    data = controller_data
         | 
| 20 | 
            +
                    return nil unless data.present?
         | 
| 21 | 
            +
                    AwesomeExplain::Controller.find_or_create_by({
         | 
| 22 | 
            +
                      name: controller_data[:controller],
         | 
| 23 | 
            +
                      action: controller_data[:action],
         | 
| 24 | 
            +
                      path: controller_data[:path],
         | 
| 25 | 
            +
                      params: controller_data[:params].to_json,
         | 
| 26 | 
            +
                      session_id: Thread.current['ae_session_id']
         | 
| 27 | 
            +
                    }).id
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def resolve_sidekiq_class_id
         | 
| 31 | 
            +
                    return unless Thread.current[:sidekiq_worker_class].present?
         | 
| 32 | 
            +
                    sidekiq_worker_class_str = Thread.current[:sidekiq_worker_class]
         | 
| 33 | 
            +
                    sidekiq_queue_str = Thread.current[:sidekiq_queue].to_s
         | 
| 34 | 
            +
                    sidekiq_worker = AwesomeExplain::SidekiqWorker.find_or_create_by({
         | 
| 35 | 
            +
                      worker: sidekiq_worker_class_str,
         | 
| 36 | 
            +
                      queue: sidekiq_queue_str,
         | 
| 37 | 
            +
                      jid: extract_sidekiq_jid(Thread.current[:sidekiq_job]),
         | 
| 38 | 
            +
                      params: Thread.current[:sidekiq_job].present? ? Thread.current[:sidekiq_job].to_json : {}
         | 
| 39 | 
            +
                    })
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    sidekiq_worker.id
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def controller_data
         | 
| 45 | 
            +
                    Thread.current['ae_controller_data']
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def extract_sidekiq_jid(args)
         | 
| 49 | 
            +
                    Thread.current[:sidekiq_job].dig('jid')
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  def resolve_source_name
         | 
| 53 | 
            +
                    Thread.current['ae_source'] || DEFAULT_SOURCE_NAME
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  def db_explain_enabled?(command_name)
         | 
| 57 | 
            +
                    return false if DML_COMMANDS.include?(command_name)
         | 
| 58 | 
            +
                    return false if command_name == :getMore
         | 
| 59 | 
            +
                    return true if Thread.current['ae_analyze']
         | 
| 60 | 
            +
                    return false if Rails.const_defined?('Console')
         | 
| 61 | 
            +
                    true
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  def db_logging_enbled?
         | 
| 65 | 
            +
                    return true if Thread.current['ae_analyze']
         | 
| 66 | 
            +
                    return false if Rails.const_defined?('Console')
         | 
| 67 | 
            +
                    true
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            module AwesomeExplain::Queue
         | 
| 2 | 
            +
              class Command
         | 
| 3 | 
            +
                attr_writer :method_name
         | 
| 4 | 
            +
                attr_writer :event
         | 
| 5 | 
            +
                attr_writer :object
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(method_name, event, object)
         | 
| 8 | 
            +
                  @method_name = method_name
         | 
| 9 | 
            +
                  @event = event
         | 
| 10 | 
            +
                  @object = object
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def run
         | 
| 14 | 
            +
                  object.send method_name, event
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         |