Weirdest bug ever (association where foreign key nil)

I found a very sharp edge case the other day in a rails 6.1 app

Essentially we have two models

class A < ApplicationRecord
  has_many :bs

  def special_bs
    bs.joins(:c).where(c: { some_field: :some_value }).select { ... }
  end
end

class B < ApplicationRecord
  belongs_to :a, optional: true
  has_one :c
end

class C < Application
end

When creating A we have an after_create callback that runs before A has been committed and calls that special_bs method. If nothing else has called the bs association first that turns out to generate a sql query that includes something along the lines of

WHERE `bs`.`a_id` = NULL

In our particular case we had a validation that was checking the contents of bs before the special_bs method ran. But we recently removed it and had every un-attached B record get assigned to the first A record that got created. (We had data audits that let us restore things once we caught this and tracked it down.)

An interesting wrinkle, I tried to write a spec for this and couldn’t reproduce it even though I can manually recreate it all day long. What I discovered is that inside of a transaction after_create callbacks have access to the eventual value of id but, outside of one they do not.

This is such an edge of an edge case that I’m not sure if it’s even worth trying to reproduce in newer rails and submit as a bug, but it was wild to track down and correct so I thought it might be worth sharing anyway.

1 Like