Commit 4a13aa9f authored by Douwe Maan's avatar Douwe Maan

Store discussion_id on Note for faster discussion lookup.

parent f3acf9fd
...@@ -5,8 +5,6 @@ class Projects::DiscussionsController < Projects::ApplicationController ...@@ -5,8 +5,6 @@ class Projects::DiscussionsController < Projects::ApplicationController
before_action :authorize_resolve_discussion! before_action :authorize_resolve_discussion!
def resolve def resolve
return render_404 unless discussion.resolvable?
discussion.resolve!(current_user) discussion.resolve!(current_user)
MergeRequests::ResolvedDiscussionNotificationService.new(project, current_user).execute(merge_request) MergeRequests::ResolvedDiscussionNotificationService.new(project, current_user).execute(merge_request)
...@@ -18,8 +16,6 @@ class Projects::DiscussionsController < Projects::ApplicationController ...@@ -18,8 +16,6 @@ class Projects::DiscussionsController < Projects::ApplicationController
end end
def unresolve def unresolve
return render_404 unless discussion.resolvable?
discussion.unresolve! discussion.unresolve!
render json: { render json: {
...@@ -34,7 +30,7 @@ class Projects::DiscussionsController < Projects::ApplicationController ...@@ -34,7 +30,7 @@ class Projects::DiscussionsController < Projects::ApplicationController
end end
def discussion def discussion
@discussion ||= @merge_request.find_discussion(params[:id]) || render_404 @discussion ||= @merge_request.find_diff_discussion(params[:id]) || render_404
end end
def authorize_resolve_discussion! def authorize_resolve_discussion!
......
...@@ -49,7 +49,7 @@ module NotesHelper ...@@ -49,7 +49,7 @@ module NotesHelper
} }
if use_legacy_diff_note if use_legacy_diff_note
discussion_id = LegacyDiffNote.build_discussion_id( discussion_id = LegacyDiffNote.discussion_id(
@comments_target[:noteable_type], @comments_target[:noteable_type],
@comments_target[:noteable_id] || @comments_target[:commit_id], @comments_target[:noteable_id] || @comments_target[:commit_id],
line_code line_code
...@@ -57,10 +57,10 @@ module NotesHelper ...@@ -57,10 +57,10 @@ module NotesHelper
data.merge!( data.merge!(
note_type: LegacyDiffNote.name, note_type: LegacyDiffNote.name,
discussion_id: Digest::SHA1.hexdigest(discussion_id) discussion_id: discussion_id
) )
else else
discussion_id = DiffNote.build_discussion_id( discussion_id = DiffNote.discussion_id(
@comments_target[:noteable_type], @comments_target[:noteable_type],
@comments_target[:noteable_id] || @comments_target[:commit_id], @comments_target[:noteable_id] || @comments_target[:commit_id],
position position
...@@ -69,7 +69,7 @@ module NotesHelper ...@@ -69,7 +69,7 @@ module NotesHelper
data.merge!( data.merge!(
position: position.to_json, position: position.to_json,
note_type: DiffNote.name, note_type: DiffNote.name,
discussion_id: Digest::SHA1.hexdigest(discussion_id) discussion_id: discussion_id
) )
end end
......
...@@ -15,8 +15,9 @@ class DiffNote < Note ...@@ -15,8 +15,9 @@ class DiffNote < Note
validate :positions_complete validate :positions_complete
validate :verify_supported validate :verify_supported
after_initialize :ensure_original_discussion_id
before_validation :set_original_position, :update_position, on: :create before_validation :set_original_position, :update_position, on: :create
before_validation :set_line_code before_validation :set_line_code, :set_original_discussion_id
after_save :keep_around_commits after_save :keep_around_commits
class << self class << self
...@@ -33,14 +34,6 @@ class DiffNote < Note ...@@ -33,14 +34,6 @@ class DiffNote < Note
{ position: position.to_json } { position: position.to_json }
end end
def discussion_id
@discussion_id ||= Digest::SHA1.hexdigest(self.class.build_discussion_id(noteable_type, noteable_id || commit_id, position))
end
def original_discussion_id
@original_discussion_id ||= Digest::SHA1.hexdigest(self.class.build_discussion_id(noteable_type, noteable_id || commit_id, original_position))
end
def position=(new_position) def position=(new_position)
if new_position.is_a?(String) if new_position.is_a?(String)
new_position = JSON.parse(new_position) rescue nil new_position = JSON.parse(new_position) rescue nil
...@@ -106,11 +99,7 @@ class DiffNote < Note ...@@ -106,11 +99,7 @@ class DiffNote < Note
def discussion def discussion
return unless resolvable? return unless resolvable?
discussion_notes = self.noteable.notes.fresh.select { |n| n.discussion_id == self.discussion_id } self.noteable.find_diff_discussion(self.discussion_id)
return if discussion_notes.empty?
Discussion.new(discussion_notes)
end end
def to_discussion def to_discussion
...@@ -139,6 +128,26 @@ class DiffNote < Note ...@@ -139,6 +128,26 @@ class DiffNote < Note
self.line_code = self.position.line_code(self.project.repository) self.line_code = self.position.line_code(self.project.repository)
end end
def ensure_original_discussion_id
return unless self.persisted?
return if self.original_discussion_id
set_original_discussion_id
update_column(:original_discussion_id, self.original_discussion_id)
end
def set_original_discussion_id
self.original_discussion_id = Digest::SHA1.hexdigest(build_original_discussion_id)
end
def build_discussion_id
self.class.build_discussion_id(noteable_type, noteable_id || commit_id, position)
end
def build_original_discussion_id
self.class.build_discussion_id(noteable_type, noteable_id || commit_id, original_position)
end
def update_position def update_position
return unless supported? return unless supported?
return if for_commit? return if for_commit?
......
...@@ -57,7 +57,7 @@ class Discussion ...@@ -57,7 +57,7 @@ class Discussion
def id def id
first_note.discussion_id first_note.discussion_id
end end
alias_method :to_param, :id alias_method :to_param, :id
def diff_discussion? def diff_discussion?
...@@ -93,7 +93,7 @@ class Discussion ...@@ -93,7 +93,7 @@ class Discussion
return false unless resolvable? return false unless resolvable?
current_user == self.noteable.author || current_user == self.noteable.author ||
current_user.can?(:push_code, self.project) current_user.can?(:resolve_note, self.project)
end end
def resolve!(current_user) def resolve!(current_user)
......
...@@ -8,8 +8,8 @@ class LegacyDiffNote < Note ...@@ -8,8 +8,8 @@ class LegacyDiffNote < Note
before_create :set_diff before_create :set_diff
class << self class << self
def build_discussion_id(noteable_type, noteable_id, line_code, active = true) def build_discussion_id(noteable_type, noteable_id, line_code)
[super(noteable_type, noteable_id), line_code, active].join("-") [super(noteable_type, noteable_id), line_code].join("-")
end end
end end
...@@ -21,10 +21,6 @@ class LegacyDiffNote < Note ...@@ -21,10 +21,6 @@ class LegacyDiffNote < Note
{ line_code: line_code } { line_code: line_code }
end end
def discussion_id
@discussion_id ||= Digest::SHA1.hexdigest(self.class.build_discussion_id(noteable_type, noteable_id || commit_id, line_code))
end
def project_repository def project_repository
if RequestStore.active? if RequestStore.active?
RequestStore.fetch("project:#{project_id}:repository") { self.project.repository } RequestStore.fetch("project:#{project_id}:repository") { self.project.repository }
...@@ -119,4 +115,8 @@ class LegacyDiffNote < Note ...@@ -119,4 +115,8 @@ class LegacyDiffNote < Note
diffs = noteable.raw_diffs(Commit.max_diff_options) diffs = noteable.raw_diffs(Commit.max_diff_options)
diffs.find { |d| d.new_path == self.diff.new_path } diffs.find { |d| d.new_path == self.diff.new_path }
end end
def build_discussion_id
self.class.build_discussion_id(noteable_type, noteable_id || commit_id, line_code)
end
end end
...@@ -425,16 +425,23 @@ class MergeRequest < ActiveRecord::Base ...@@ -425,16 +425,23 @@ class MergeRequest < ActiveRecord::Base
discussions discussions
end end
def find_discussion(discussion_id) def diff_discussions
discussions.find { |d| d.id == discussion_id } @diff_discussions ||= self.notes.diff_notes.discussions
end
def find_diff_discussion(discussion_id)
notes = self.notes.diff_notes.where(discussion_id: discussion_id).fresh.to_a
return if notes.empty?
Discussion.new(notes)
end end
def discussions_resolvable? def discussions_resolvable?
discussions.any?(&:resolvable?) diff_discussions.any?(&:resolvable?)
end end
def discussions_resolved? def discussions_resolved?
discussions_resolvable? && discussions.none?(&:to_be_resolved?) discussions_resolvable? && diff_discussions.none?(&:to_be_resolved?)
end end
def hook_attrs def hook_attrs
......
...@@ -70,7 +70,9 @@ class Note < ActiveRecord::Base ...@@ -70,7 +70,9 @@ class Note < ActiveRecord::Base
project: [:project_members, { group: [:group_members] }]) project: [:project_members, { group: [:group_members] }])
end end
after_initialize :ensure_discussion_id
before_validation :nullify_blank_type, :nullify_blank_line_code before_validation :nullify_blank_type, :nullify_blank_line_code
before_validation :set_discussion_id
after_save :keep_around_commit after_save :keep_around_commit
class << self class << self
...@@ -82,6 +84,10 @@ class Note < ActiveRecord::Base ...@@ -82,6 +84,10 @@ class Note < ActiveRecord::Base
[:discussion, noteable_type.try(:underscore), noteable_id].join("-") [:discussion, noteable_type.try(:underscore), noteable_id].join("-")
end end
def self.discussion_id(*args)
Digest::SHA1.hexdigest(build_discussion_id(*args))
end
def discussions def discussions
Discussion.for_notes(all) Discussion.for_notes(all)
end end
...@@ -142,15 +148,6 @@ class Note < ActiveRecord::Base ...@@ -142,15 +148,6 @@ class Note < ActiveRecord::Base
resolvable? && !resolved? resolvable? && !resolved?
end end
def discussion_id
@discussion_id ||=
if for_merge_request?
Digest::SHA1.hexdigest([:discussion, :note, id].join("-"))
else
Digest::SHA1.hexdigest(self.class.build_discussion_id(noteable_type, noteable_id || commit_id))
end
end
def max_attachment_size def max_attachment_size
current_application_settings.max_attachment_size.megabytes.to_i current_application_settings.max_attachment_size.megabytes.to_i
end end
...@@ -256,4 +253,24 @@ class Note < ActiveRecord::Base ...@@ -256,4 +253,24 @@ class Note < ActiveRecord::Base
def nullify_blank_line_code def nullify_blank_line_code
self.line_code = nil if self.line_code.blank? self.line_code = nil if self.line_code.blank?
end end
def ensure_discussion_id
return unless self.persisted?
return if self.discussion_id
set_discussion_id
update_column(:discussion_id, self.discussion_id)
end
def set_discussion_id
self.discussion_id = Digest::SHA1.hexdigest(build_discussion_id)
end
def build_discussion_id
if for_merge_request?
[:discussion, :note, id].join("-")
else
self.class.build_discussion_id(noteable_type, noteable_id || commit_id)
end
end
end end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddDiscussionIdsToNotes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :notes, :discussion_id, :string
add_column :notes, :original_discussion_id, :string
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160810142633) do ActiveRecord::Schema.define(version: 20160817154936) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -663,7 +663,7 @@ ActiveRecord::Schema.define(version: 20160810142633) do ...@@ -663,7 +663,7 @@ ActiveRecord::Schema.define(version: 20160810142633) do
t.string "line_code" t.string "line_code"
t.string "commit_id" t.string "commit_id"
t.integer "noteable_id" t.integer "noteable_id"
t.boolean "system", default: false, null: false t.boolean "system", default: false, null: false
t.text "st_diff" t.text "st_diff"
t.integer "updated_by_id" t.integer "updated_by_id"
t.string "type" t.string "type"
...@@ -671,6 +671,8 @@ ActiveRecord::Schema.define(version: 20160810142633) do ...@@ -671,6 +671,8 @@ ActiveRecord::Schema.define(version: 20160810142633) do
t.text "original_position" t.text "original_position"
t.datetime "resolved_at" t.datetime "resolved_at"
t.integer "resolved_by_id" t.integer "resolved_by_id"
t.string "discussion_id"
t.string "original_discussion_id"
end end
add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
......
...@@ -434,4 +434,54 @@ describe DiffNote, models: true do ...@@ -434,4 +434,54 @@ describe DiffNote, models: true do
end end
end end
end end
describe "#discussion_id" do
let(:note) { create(:diff_note_on_merge_request) }
context "when it is newly created" do
it "has a discussion id" do
expect(note.discussion_id).not_to be_nil
expect(note.discussion_id).to match(/\A\h{40}\z/)
end
end
context "when it didn't store a discussion id before" do
before do
note.update_column(:discussion_id, nil)
end
it "has a discussion id" do
# The discussion_id is set in `after_initialize`, so `reload` won't work
reloaded_note = Note.find(note.id)
expect(reloaded_note.discussion_id).not_to be_nil
expect(reloaded_note.discussion_id).to match(/\A\h{40}\z/)
end
end
end
describe "#original_discussion_id" do
let(:note) { create(:diff_note_on_merge_request) }
context "when it is newly created" do
it "has a discussion id" do
expect(note.original_discussion_id).not_to be_nil
expect(note.original_discussion_id).to match(/\A\h{40}\z/)
end
end
context "when it didn't store a discussion id before" do
before do
note.update_column(:original_discussion_id, nil)
end
it "has a discussion id" do
# The original_discussion_id is set in `after_initialize`, so `reload` won't work
reloaded_note = Note.find(note.id)
expect(reloaded_note.original_discussion_id).not_to be_nil
expect(reloaded_note.original_discussion_id).to match(/\A\h{40}\z/)
end
end
end
end end
...@@ -73,4 +73,29 @@ describe LegacyDiffNote, models: true do ...@@ -73,4 +73,29 @@ describe LegacyDiffNote, models: true do
end end
end end
end end
describe "#discussion_id" do
let(:note) { create(:note) }
context "when it is newly created" do
it "has a discussion id" do
expect(note.discussion_id).not_to be_nil
expect(note.discussion_id).to match(/\A\h{40}\z/)
end
end
context "when it didn't store a discussion id before" do
before do
note.update_column(:discussion_id, nil)
end
it "has a discussion id" do
# The discussion_id is set in `after_initialize`, so `reload` won't work
reloaded_note = Note.find(note.id)
expect(reloaded_note.discussion_id).not_to be_nil
expect(reloaded_note.discussion_id).to match(/\A\h{40}\z/)
end
end
end
end end
...@@ -763,7 +763,7 @@ describe MergeRequest, models: true do ...@@ -763,7 +763,7 @@ describe MergeRequest, models: true do
let(:third_discussion) { Discussion.new([create(:diff_note_on_merge_request)]) } let(:third_discussion) { Discussion.new([create(:diff_note_on_merge_request)]) }
before do before do
allow(subject).to receive(:discussions).and_return([first_discussion, second_discussion, third_discussion]) allow(subject).to receive(:diff_discussions).and_return([first_discussion, second_discussion, third_discussion])
end end
describe "#discussions_resolvable?" do describe "#discussions_resolvable?" do
......
...@@ -321,4 +321,29 @@ describe Note, models: true do ...@@ -321,4 +321,29 @@ describe Note, models: true do
expect(subject[active_diff_note3.line_code].id).to eq(active_diff_note3.discussion_id) expect(subject[active_diff_note3.line_code].id).to eq(active_diff_note3.discussion_id)
end end
end end
describe "#discussion_id" do
let(:note) { create(:note) }
context "when it is newly created" do
it "has a discussion id" do
expect(note.discussion_id).not_to be_nil
expect(note.discussion_id).to match(/\A\h{40}\z/)
end
end
context "when it didn't store a discussion id before" do
before do
note.update_column(:discussion_id, nil)
end
it "has a discussion id" do
# The discussion_id is set in `after_initialize`, so `reload` won't work
reloaded_note = Note.find(note.id)
expect(reloaded_note.discussion_id).not_to be_nil
expect(reloaded_note.discussion_id).to match(/\A\h{40}\z/)
end
end
end
end end
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