ruby-on-rails

Unresolvable trigger of N+1 query


The following method is invoked for a collection. It handles only integers:

def create_txrow( parti_id, paying_consumer_id, nation_vat_id )
  puts nation_vat_id
  puts nation_vat_id.class
  puts parti_id
  puts parti_id.class
      new_row = Txrow.create(
        nation_vat_id: nation_vat_id,
        paying_consumer_id: paying_consumer_id,
        parti_id: parti_id,
[...]

The console output returns as expected

1
Integer
2
Integer

Yet the log shows for each iteration of this method

 ↳ app/controllers/concerns/taxation_methods.rb:253:in `create_txrow'
  NationVat Load (0.5ms)  SELECT "nation_vats".* FROM "nation_vats" WHERE "nation_vats"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/concerns/taxation_methods.rb:253:in `create_txrow'
  Parti Load (0.6ms)  SELECT "partis".* FROM "partis" WHERE "partis"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
  ↳ app/controllers/concerns/taxation_methods.rb:253:in `create_txrow'

Line 253 referes specifically to new_row = Txrow.create( line.

Thus these queries are being made to the database, but nothing in the code is requesting it... the integers should be nominally be fed to the method.

To avoid the N+1 problem, the collection is instantiated by a query that specifies
.includes( :parti, :shopvat in order to then select on the basis of the member of that collection
shopvat_for_item = shopvats.select{ |i| i.id = item.shopvat_id }.first and generate the call to this method with
new_txrow = create_txrow( item.parti_id, item.cover.consumer_id, shopvat_for_item.nation_vat_id).

What could possibly trigger two db queries (while not the third!)


Solution

  • These checks are for referential integrity. They ensure that the NationVat and the Parti exist based on the belongs_to relationship.

    If you don't want this validation (which seems like a bad idea in this case) you can set optional: true on the belongs_to relationship.

    class Txrow < ApplicationRecord
      belongs_to :nation_vat, optional: true
      belongs_to :parti, optional: true
    end 
    

    Again given that Txrow seems to be an associative join between the NationVat and the Parti objects, I would not recommend making these optional but this will stop the queries you are seeing.