Commit dac51ace authored by Yorick Peterse's avatar Yorick Peterse

Eager load event target authors whenever possible

This ensures that the "author" association of an event's "target"
association is eager loaded whenever the "target" association defines an
"author" association. This in turn solves the N+1 query problem we first
tried to solve in
https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15788 but caused
problems when displaying milestones as those don't define an "author"
association.

The approach in this commit does mean that the authors are _always_
eager loaded since this takes place in the "belongs_to" block. This
however shouldn't pose too much of a problem, and as far as I can tell
there's no real way around this unfortunately.
parent 1dac4271
...@@ -48,7 +48,18 @@ class Event < ActiveRecord::Base ...@@ -48,7 +48,18 @@ class Event < ActiveRecord::Base
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
belongs_to :project belongs_to :project
belongs_to :target, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
belongs_to :target, -> {
# If the association for "target" defines an "author" association we want to
# eager-load this so Banzai & friends don't end up performing N+1 queries to
# get the authors of notes, issues, etc.
if reflections['events'].active_record.reflect_on_association(:author)
includes(:author)
else
self
end
}, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
has_one :push_event_payload has_one :push_event_payload
# Callbacks # Callbacks
......
---
title: Eager load event target authors whenever possible
merge_request:
author:
type: performance
...@@ -24,6 +24,7 @@ feature 'Dashboard > Activity' do ...@@ -24,6 +24,7 @@ feature 'Dashboard > Activity' do
end end
let(:note) { create(:note, project: project, noteable: merge_request) } let(:note) { create(:note, project: project, noteable: merge_request) }
let(:milestone) { create(:milestone, :active, project: project, title: '1.0') }
let!(:push_event) do let!(:push_event) do
event = create(:push_event, project: project, author: user) event = create(:push_event, project: project, author: user)
...@@ -54,6 +55,10 @@ feature 'Dashboard > Activity' do ...@@ -54,6 +55,10 @@ feature 'Dashboard > Activity' do
create(:event, :commented, project: project, target: note, author: user) create(:event, :commented, project: project, target: note, author: user)
end end
let!(:milestone_event) do
create(:event, :closed, project: project, target: milestone, author: user)
end
before do before do
project.add_master(user) project.add_master(user)
...@@ -68,6 +73,7 @@ feature 'Dashboard > Activity' do ...@@ -68,6 +73,7 @@ feature 'Dashboard > Activity' do
expect(page).to have_content('accepted') expect(page).to have_content('accepted')
expect(page).to have_content('closed') expect(page).to have_content('closed')
expect(page).to have_content('commented on') expect(page).to have_content('commented on')
expect(page).to have_content('closed milestone')
end end
end end
...@@ -107,6 +113,7 @@ feature 'Dashboard > Activity' do ...@@ -107,6 +113,7 @@ feature 'Dashboard > Activity' do
expect(page).not_to have_content('accepted') expect(page).not_to have_content('accepted')
expect(page).to have_content('closed') expect(page).to have_content('closed')
expect(page).not_to have_content('commented on') expect(page).not_to have_content('commented on')
expect(page).to have_content('closed milestone')
end end
end end
......
...@@ -347,6 +347,22 @@ describe Event do ...@@ -347,6 +347,22 @@ describe Event do
end end
end end
describe '#target' do
it 'eager loads the author of an event target' do
create(:closed_issue_event)
events = described_class.preload(:target).all.to_a
count = ActiveRecord::QueryRecorder
.new { events.first.target.author }.count
# This expectation exists to make sure the test doesn't pass when the
# author is for some reason not loaded at all.
expect(events.first.target.author).to be_an_instance_of(User)
expect(count).to be_zero
end
end
def create_push_event(project, user) def create_push_event(project, user)
event = create(:push_event, project: project, author: user) event = create(:push_event, project: project, author: user)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment