Commit 37c5bbb9 authored by Dmytro Zaporozhets's avatar Dmytro Zaporozhets

Merge branch 'extract-unique-internal-into-a-module' into 'master'

Extract unique_internal to a module

See merge request gitlab-org/gitlab!27386
parents e75febea 402fb043
# 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 = 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
......@@ -21,6 +21,7 @@ class User < ApplicationRecord
include OptionallySearch
include FromUnion
include BatchDestroyDependentAssociations
include HasUniqueInternalUsers
include IgnorableColumns
DEFAULT_NOTIFICATION_LEVEL = :participating
......@@ -1793,48 +1794,6 @@ class User < ApplicationRecord
end
end
def self.unique_internal(scope, username, email_pattern, &block)
scope.first || create_unique_internal(scope, username, email_pattern, &block)
end
def self.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 = 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) # rubocop: disable CodeReuse/ServiceClass
user
ensure
Gitlab::ExclusiveLease.cancel(lease_key, uuid)
end
def groups_with_developer_maintainer_project_access
project_creation_levels = [::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS]
......
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