Reputation: 1129
The following code returns a unique 3 character code by continually checking if the genereated code already exists in the db. Once it finds one that does not exist the loop exits.
How can I protect against race conditions which could lead to non-unique codes being returned?
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)
Upvotes: 1
Views: 529
Reputation: 11638
How can I protect against race conditions which could lead to non-unique codes being returned?
Don't use the database as a synchronization point. Apart from synchronization issues, your code is susceptible to slowdown as the number of available codes shrinks. There is no guarantee your loop would terminate.
A far better approach to this would be to have a service which pre-generates a batch of unique identifiers and hands these out on a first-come, first-served basis.
Given that you are only using 3 characters for this code, you can only store ~= 17 000 records - you could generate the entire list of permutations of three character codes up front, and remove entries from this list as you allocate them.
Upvotes: 4
Reputation: 168081
Preventing a race is done by putting the process in a mutex:
@mutex = Mutex.new
within the method that calls your code:
@mutex.synchronize do
# Whatever process you want to avoid race
end
But a problem with your approach is that your loop may never end since you are using randomness.
Upvotes: 0
Reputation: 106802
You can add a unique index on the database column, and then just try to update a Pubcode
with a random uuid. If that fails because of the unique index, just try another code:
pubcode = Pubcode.find_by_pub_id!(current_pub.id)
begin
pupcode.update!(guid: SecureRandom.hex(2)[0..2])
rescue ActiveRecord::StatementInvalid => e
retry
end
Perhaps you want to count the number of retries and raise the exception if there was no code found within a certain number of tries (because there are only 4096 possible ids).
Upvotes: 1