Commit 99acb6ae authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Add store_mentions_without_subtransaction FF

Adds a feature flag to the optimization done for store_mentions!
parent 221d5414
...@@ -160,6 +160,39 @@ module CacheMarkdownField ...@@ -160,6 +160,39 @@ module CacheMarkdownField
# We can only store mentions if the mentionable is a database object # We can only store mentions if the mentionable is a database object
return unless self.is_a?(ApplicationRecord) return unless self.is_a?(ApplicationRecord)
return store_mentions_without_subtransaction! if Feature.enabled?(:store_mentions_without_subtransaction, default_enabled: :yaml)
refs = all_references(self.author)
references = {}
references[:mentioned_users_ids] = refs.mentioned_user_ids.presence
references[:mentioned_groups_ids] = refs.mentioned_group_ids.presence
references[:mentioned_projects_ids] = refs.mentioned_project_ids.presence
# One retry is enough as next time `model_user_mention` should return the existing mention record,
# that threw the `ActiveRecord::RecordNotUnique` exception in first place.
self.class.safe_ensure_unique(retries: 1) do
user_mention = model_user_mention
# this may happen due to notes polymorphism, so noteable_id may point to a record
# that no longer exists as we cannot have FK on noteable_id
break if user_mention.blank?
user_mention.mentioned_users_ids = references[:mentioned_users_ids]
user_mention.mentioned_groups_ids = references[:mentioned_groups_ids]
user_mention.mentioned_projects_ids = references[:mentioned_projects_ids]
if user_mention.has_mentions?
user_mention.save!
else
user_mention.destroy!
end
end
true
end
def store_mentions_without_subtransaction!
identifier = user_mention_identifier identifier = user_mention_identifier
# this may happen due to notes polymorphism, so noteable_id may point to a record # this may happen due to notes polymorphism, so noteable_id may point to a record
......
...@@ -217,6 +217,17 @@ module Mentionable ...@@ -217,6 +217,17 @@ module Mentionable
def user_mention_association def user_mention_association
association(:user_mentions).reflection association(:user_mentions).reflection
end end
# User mention that is parsed from model description rather then its related notes.
# Models that have a description attribute like Issue, MergeRequest, Epic, Snippet may have such a user mention.
# Other mentionable models like Commit, DesignManagement::Design, will never have such record as those do not have
# a description attribute.
#
# Using this method followed by a call to *save* may result in *ActiveRecord::RecordNotUnique* exception
# in a multi-threaded environment. Make sure to use it within a *safe_ensure_unique* block.
def model_user_mention
user_mentions.where(note_id: nil).first_or_initialize
end
end end
Mentionable.prepend_mod_with('Mentionable') Mentionable.prepend_mod_with('Mentionable')
...@@ -603,6 +603,14 @@ class Note < ApplicationRecord ...@@ -603,6 +603,14 @@ class Note < ApplicationRecord
private private
# Using this method followed by a call to *save* may result in *ActiveRecord::RecordNotUnique* exception
# in a multi-threaded environment. Make sure to use it within a *safe_ensure_unique* block.
def model_user_mention
return if user_mentions.is_a?(ActiveRecord::NullRelation)
user_mentions.first_or_initialize
end
def system_note_viewable_by?(user) def system_note_viewable_by?(user)
return true unless system_note_metadata return true unless system_note_metadata
......
---
name: store_mentions_without_subtransaction
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68433
rollout_issue_url:
milestone: '14.3'
type: development
group: group::project management
default_enabled: false
...@@ -207,7 +207,7 @@ RSpec.shared_examples 'an editable mentionable' do ...@@ -207,7 +207,7 @@ RSpec.shared_examples 'an editable mentionable' do
end end
RSpec.shared_examples 'mentions in description' do |mentionable_type| RSpec.shared_examples 'mentions in description' do |mentionable_type|
describe 'when storing user mentions' do shared_examples 'when storing user mentions' do
before do before do
mentionable.store_mentions! mentionable.store_mentions!
end end
...@@ -238,10 +238,26 @@ RSpec.shared_examples 'mentions in description' do |mentionable_type| ...@@ -238,10 +238,26 @@ RSpec.shared_examples 'mentions in description' do |mentionable_type|
end end
end end
end end
context 'when store_mentions_without_subtransaction is enabled' do
before do
stub_feature_flags(store_mentions_without_subtransaction: true)
end
it_behaves_like 'when storing user mentions'
end
context 'when store_mentions_without_subtransaction is disabled' do
before do
stub_feature_flags(store_mentions_without_subtransaction: false)
end
it_behaves_like 'when storing user mentions'
end
end end
RSpec.shared_examples 'mentions in notes' do |mentionable_type| RSpec.shared_examples 'mentions in notes' do |mentionable_type|
context 'when mentionable notes contain mentions' do shared_examples 'when mentionable notes contain mentions' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user2) { create(:user) } let(:user2) { create(:user) }
let(:group) { create(:group) } let(:group) { create(:group) }
...@@ -261,6 +277,22 @@ RSpec.shared_examples 'mentions in notes' do |mentionable_type| ...@@ -261,6 +277,22 @@ RSpec.shared_examples 'mentions in notes' do |mentionable_type|
expect(mentionable.referenced_groups(user)).to eq [group] expect(mentionable.referenced_groups(user)).to eq [group]
end end
end end
context 'when store_mentions_without_subtransaction is enabled' do
before do
stub_feature_flags(store_mentions_without_subtransaction: true)
end
it_behaves_like 'when mentionable notes contain mentions'
end
context 'when store_mentions_without_subtransaction is disabled' do
before do
stub_feature_flags(store_mentions_without_subtransaction: false)
end
it_behaves_like 'when mentionable notes contain mentions'
end
end end
RSpec.shared_examples 'load mentions from DB' do |mentionable_type| RSpec.shared_examples 'load mentions from DB' do |mentionable_type|
......
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