bullet icon indicating copy to clipboard operation
bullet copied to clipboard

Wrong `Eager Loading Detected` if use `before_all` or `let_it_be`

Open chaomao opened this issue 11 months ago • 2 comments

Hi, zm

recently I start refactoring our test, move large object creation from let to let_it_be, which will change object creation from before_each hook into before_all hook, then I found some tests failed due to eager loading detected, like below

RSpec.describe Post, type: :model do
  # post has_many comments
  # comment belongs_to post
  # creator belongs_to comment
  let_it_be(:post) { create(:post) }
  let_it_be(:comment) { create(:comment, post: post)}

  # before(:all) do # same as let_it_be
  #   puts "start before all"
  #   @post = create(:post)
  #   @comment = create(:comment, post: @post)
  #   puts "end before all"
  # end

  it 'ok' do
    creator = create(:creator, comment_id: comment.id)
    comment.post # ok
  end

  it 'raise N+1 query issue' do
    creator = create(:creator, comment: comment)
    comment.post # will raise eager loading Comment => [:post]
  end
end

which will raise error USE eager loading detected, Comment => [:post], Add to your query: .includes([:post]), reproducable repo is here. I don't think the detection is correct :(

I read the source code of bullet and found the issue is the object created in before_all hook didn't add into Bullet::Detector::NPlusOneQuery::impossible_objects, because Bullet.start_request is invoked in before_each hook.

# n_plus_one_query.rb
        def add_impossible_object(object)
          return unless Bullet.start? # will return when object creation in `before_all` hook
          return unless Bullet.n_plus_one_query_enable?
          return unless object.bullet_primary_key_value

          Bullet.debug('Detector::NPlusOneQuery#add_impossible_object', "object: #{object.bullet_key}")
          impossible_objects.add object.bullet_key
        end

# spec_helper.rb, will start Bullet in before_each hook.
if Bullet.enable?
  RSpec.configure do |config|
    config.before do
      Bullet.start_request
    end

    config.after do
      Bullet.perform_out_of_channel_notifications if Bullet.notification?
      Bullet.end_request
    end
  end
end

so my questions are:

  1. is there a way to avoid eager loading detected error when I use before_all or let_it_be? I really want to move some large object creation out of each test, which will significantly improve performance
  2. no matter the problem is solved or not, I hope the documentation can cover this scenario, it may help other people on it :)

chaomao avatar Aug 15 '23 06:08 chaomao