生成唯一代码时防止竞争条件

时间:2014-07-10 09:37:54

标签: ruby ruby-on-rails-4 race-condition

以下代码通过不断检查数据库中是否已存在生成的代码来返回唯一的3字符代码。一旦找到一个不存在的循环退出。

如何防止可能导致返回非唯一代码的竞争条件?

               pubcode = Pubcode.find_by_pub_id(current_pub.id)

               new_id = nil  
               begin              
                  new_id = SecureRandom.hex(2)[0..2].to_s 
                  old_id = Pubcode.find_by_guid(new_id) 
                  if !old_id.nil? 
                   pubcode.guid = new_id
                   pubcode.save
                  end
                end while (old_id)

3 个答案:

答案 0 :(得分:4)

  

如何防止可能导致返回非唯一代码的竞争条件?

不要将数据库用作同步点。除了同步问题之外,随着可用代码数量的减少,您的代码很容易减速。无法保证您的循环将终止。

更好的方法是使用预先生成一批唯一标识符的服务,并按照先到先得的原则提供这些服务。

鉴于此代码仅使用3个字符,您只能存储〜= 17 000条记录 - 您可以预先生成三个字符代码的整个排列列表,并在分配它们时从此列表中删除条目

答案 1 :(得分:1)

您可以在数据库列上添加唯一索引,然后尝试使用随机uuid更新Pubcode。如果由于唯一索引而失败,只需尝试另一个代码:

pubcode = Pubcode.find_by_pub_id!(current_pub.id)

begin
  pupcode.update!(guid: SecureRandom.hex(2)[0..2])
rescue ActiveRecord::StatementInvalid => e
  retry 
end

如果在一定次数的尝试中找不到代码,可能需要计算重试次数并引发异常(因为只有4096个可能的ID)。

答案 2 :(得分:0)

通过将进程置于互斥锁中来防止竞争:

@mutex = Mutex.new

在调用代码的方法中:

@mutex.synchronize do
  # Whatever process you want to avoid race
end

但是你的方法存在问题,因为你使用随机性,你的循环可能永远不会结束。