Issue associations not automatically set when there is an award emoji
This issue was first discovered via import code and fixed for imports via this MR: !131958 (merged)
The fix includes saving all subrelations separately from the parent. This results in child records being persisted during imports but does not fix the underlying issue, which is unexpected behavior when persisting Issue record subrealtions.
What I expect to see
when an issue is initialized alongside many subrelations, such as award_emoji, assignees, notes, and labels, and the issue is saved and the subrelations should all save as well. This is default Rails behavior.
What I see instead
Instead, if an award emoji is present, only the award emoji will save. All other subrelations fail to save but without any errors.
i = FactoryBot.build(:issue, author: User.first, notes: [FactoryBot.build(:note, importing: true)], award_emoji: [FactoryBot.build(:award_emoji)], project: Project.last)
i.save!
=> issue saved, award_emoji saved, note not saved
More detail
Reproduction via RSpec
One thing that is confusing me so far is that this happens on an Issue but not other "awardable" objects, such as Merge Requests or Snippets.
Here is a spec that shows how subrelation persistence failure is happening (this spec failures for the issue awardable but not the other types)
# spec/models/concerns/awardable_spec.rb
context "awardable item is persisted alongside award emoji and other relations", :aggregate_failures do
where(awardable_type: [:issue, :merge_request, :snippet])
with_them do
it "saves all subrelations" do
awardable = build(awardable_type)
note = build(:note, importing: true)
award_emoji = build(:award_emoji)
awardable.award_emoji << award_emoji
awardable.notes << note
awardable.save!
expect(awardable).to be_persisted
expect(awardable.award_emoji.count).to eq 1
expect(awardable.notes.count).to eq 1
end
end
end
This is how the spec fails:
Failures:
1) Awardable Associations awardable item is persisted alongside award emoji and other relations awardable_type: :issue saves all subrelations
Failure/Error: expect(awardable.notes.count).to eq 1
expected: 1
got: 0
Finished in 11.72 seconds (files took 2.2 seconds to load)
3 examples, 1 failure
So the issue and award emoji are persisted, but not the note. The same thing happens for any other has_many
relations on an issue that are new records on the issue at the time that issue.save!
is called. So if you try to initialize a new label and associate that with the issue, it will also not save if there is also a new award emoji being persisted on the issue at the same time.
inverse_of
Fix via removing Again, this is for issues only. The key elements here seem to be Issue + Award emoji. The specs pass for merge requests and snippets, which also have has_many
relationships with award emoji and notes via the Issuable
concern.
I do know how to fix this problem, but I do not understand why. Currently, Awardable
, which is included in the Issuable
concern, which is included in MergeRequest
, Issue
, and Snippet
models, defines the relation like this:
has_many :award_emoji, -> { includes(:user).order(:id) }, as: :awardable, inverse_of: :awardable, dependent: :destroy
If I remove inverse_of: awardable
here, the spec above passes. This was the best blog post I could find about inverse_of
So the key thing might be our use of inverse_of
. I've dug through the rails/rails
history and here are some issues that feel related to this bug (but no exact matches):
- "On has_many_inversing object_id has different values after create" (issue) - issue closed without a fix
- "Rails 6.1 double assignment leads to duplicate in-memory associations" (issue) (MR with fix)
- "collection= for a has_many association doesn't persist added records already referencing the parent when has_many_inversing is enabled" (issue) (we do not have
has_many_inversing
enabled but having that option enabled is just an implicit globalinverse_of:
for all associations, as far as I can tell, which would mean this is an error related toinverse_of:
being set)