51 lines
1.6 KiB
Ruby
51 lines
1.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module HasUniqueInternalUsers
|
|
extend ActiveSupport::Concern
|
|
|
|
class_methods do
|
|
private
|
|
|
|
def unique_internal(scope, username, email_pattern, &block)
|
|
scope.first || create_unique_internal(scope, username, email_pattern, &block)
|
|
end
|
|
|
|
def create_unique_internal(scope, username, email_pattern, &creation_block)
|
|
# Since we only want a single one of these in an instance, we use an
|
|
# exclusive lease to ensure than this block is never run concurrently.
|
|
lease_key = "user:unique_internal:#{username}"
|
|
lease = Gitlab::ExclusiveLease.new(lease_key, timeout: 1.minute.to_i)
|
|
|
|
until uuid = lease.try_obtain
|
|
# Keep trying until we obtain the lease. To prevent hammering Redis too
|
|
# much we'll wait for a bit between retries.
|
|
sleep(1)
|
|
end
|
|
|
|
# Recheck if the user is already present. One might have been
|
|
# added between the time we last checked (first line of this method)
|
|
# and the time we acquired the lock.
|
|
existing_user = uncached { scope.first }
|
|
return existing_user if existing_user.present?
|
|
|
|
uniquify = Gitlab::Utils::Uniquify.new
|
|
|
|
username = uniquify.string(username) { |s| User.find_by_username(s) }
|
|
|
|
email = uniquify.string(-> (n) { Kernel.sprintf(email_pattern, n) }) do |s|
|
|
User.find_by_email(s)
|
|
end
|
|
|
|
user = scope.build(
|
|
username: username,
|
|
email: email,
|
|
&creation_block
|
|
)
|
|
|
|
Users::UpdateService.new(user, user: user).execute(validate: false)
|
|
user
|
|
ensure
|
|
Gitlab::ExclusiveLease.cancel(lease_key, uuid)
|
|
end
|
|
end
|
|
end
|