This commit is contained in:
Christian Oelschlegel 2025-09-03 20:07:43 +00:00 committed by GitHub
commit 6b7aa05c32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 29 additions and 2 deletions

View File

@ -112,8 +112,14 @@ class Tag < ApplicationRecord
names = Array(name_or_names).map { |str| [normalize(str), str] }.uniq(&:first)
names.map do |(normalized_name, display_name)|
tag = matching_name(normalized_name).first || create(name: normalized_name,
display_name: display_name.gsub(HASHTAG_INVALID_CHARS_RE, ''))
tag = begin
matching_name(normalized_name).first_or_create!(
name: normalized_name,
display_name: display_name.gsub(HASHTAG_INVALID_CHARS_RE, '')
)
rescue ActiveRecord::RecordNotUnique
find_normalized(normalized_name)
end
yield tag if block_given?

View File

@ -261,6 +261,27 @@ RSpec.describe Tag do
end
end
describe '.find_or_create_by_names_race_condition' do
it 'handles simultaneous inserts of the same tag in different cases without error' do
tag_name_upper = 'Rails'
tag_name_lower = 'rails'
threads = []
2.times do |i|
threads << Thread.new do
Tag.find_or_create_by_names(i.zero? ? tag_name_upper : tag_name_lower)
end
end
threads.each(&:join)
tags = Tag.where('lower(name) = ?', tag_name_lower.downcase)
expect(tags.count).to eq(1)
expect(tags.first.name.downcase).to eq(tag_name_lower.downcase)
end
end
describe '.search_for' do
it 'finds tag records with matching names' do
tag = Fabricate(:tag, name: 'match')