charkost / prosopite

:mag: Rails N+1 queries auto-detection with zero false positives / false negatives
Apache License 2.0
1.53k stars 46 forks source link

Allow overriding `Prosopite.raise` config to temporarily `raise` on N+1s #44

Open DanielGilchrist opened 2 years ago

DanielGilchrist commented 2 years ago

Hello and thank you for this gem!

I'm in the process of integrating Prosopite into our Rails codebase and we're going for an incremental approach by setting Prosopite.raise = false during configuration to avoid disrupting development. The problem for us with this is we also want to be able to raise on specific actions / controllers once N+1s have been eliminated from them with an around action. We considered setting config to always raise and only setting the around action for controllers / actions with N+1s resolved but we're also really keen on keeping the logs for existing N+1s

Here's an example of it's usage in a Rails app where Prosopite.raise is set to false:

class ApplicationController < ActionController::Base
  ...

  def self.raise_on_n_plus_ones!(**options)
    return unless Rails.configuration.x.prosopite.enabled?

    prepend_around_action(:_raise_on_n_plus_ones, **options)
  end

  if Rails.configuration.x.prosopite.enabled?
    around_action :n_plus_one_detection

    def n_plus_one_detection
      Prosopite.scan
      yield
    ensure
      Prosopite.finish
    end

    def _raise_on_n_plus_ones
      Prosopite.force_raise
      yield
    ensure
      Prosopite.unforce_raise
    end
  end
end

class UserController < ApplicationController
  # only raises on N+1s introduced to the index action
  raise_on_n_plus_ones!(only: [:index])

  def index
    ...
  end

  def edit
    ...
  end
end

Please let me know if you'd prefer a different API or anything else and I'll fix it up, thanks!

charkost commented 2 years ago

Hello! Wouldn't you achieve the same by using the Prosopite.raise setter?

def _raise_on_n_plus_ones
  Prosopite.raise = true
  yield
ensure
  Prosopite.raise = false
end

The setter should probably be modified to be thread safe though.

DanielGilchrist commented 2 years ago

Hello! Wouldn't you achieve the same by using the Prosopite.raise setter?

def _raise_on_n_plus_ones
  Prosopite.raise = true
  yield
ensure
  Prosopite.raise = false
end

The setter should probably be modified to be thread safe though.

Thank you for your response and sorry for the late reply!

The main reason is that the current implementation of Prosopite.raise doesn't work well for multi-threaded applications or between requests (in our case a Rails application) as it's an instance variable on the class itself. This works great for initial setup but could lead to confusing behaviour when set dynamically on the class

Happy to look into moving to a Prosopite.raise focused API if you think that would be more suitable

ghiculescu commented 1 year ago

@charkost is there anything we can do to nudge this PR along? 🙏