Reputation: 2107
I am planning to use SecureRandom.hex
to generate API keys for my users.
So far here is the output in 3 executions:
Loading development environment (Rails 5.2.1)
2.3.5 :001 > SecureRandom.hex
=> "0369e9b7c6ffa07bd8d0a263f7b4cfa6"
2.3.5 :002 > SecureRandom.hex
=> "1a8a168d7f70676451e3d59353e22693"
2.3.5 :003 > SecureRandom.hex
=> "94cc188e9e5c3abfe587510fa79993ce"
What are the chances of me getting a duplicate result?
And will this method that I created will really avoid producing duplicate content?
def generate_string
string = SecureRandom.hex
generate_string if Model.where(:key => string).count > 0
string
end
unique_string = generate_string
I am using recursion that if the string is already stored in the database, it will just produce another.
And also, since I am not getting a duplicate, how many strings can I produce with SecureRandom.hex
before it rans out of combinations to produce?
Upvotes: 0
Views: 2512
Reputation: 106882
In your example (when using SecureRandom.hex
with the default length of 32) there are
16**32 = 340282366920938463463374607431768211456
different hex values possible. That means there is a chance of 1:340282366920938463463374607431768211456
to create a duplicate. This chance is extremely low and IMHO it doesn't make much sense to worry too much about it.
When you are going to store that key in the database then I would suggest adding a unique index to that database column to ensure - on the database level - that it is impossible to store any duplicates.
Furthermore, you asked if your example code is enough to avoid duplicates. The answer is No. It is highly theoretical because of the low probability, but you might run into race conditions in which two jobs generating the same keys at the same time, checking both that there isn't such a key in the database and that both jobs store the same value into the table.
tl;dr Chance of duplicates are extremely low. Only a unique index on the key
column in the database ensures that there will never be any duplicates (because of race conditions or keys generated bypassing this method).
Upvotes: 5
Reputation: 168101
The length of the keys seem to be 32 in your setting.
The chances of not getting duplicates in three executions is:
((16**32 - 1)/16**32) * ((16**32 - 2)/16**32)
So the chances of getting (at least a pair of) duplicates somewhere in three executions is:
1 - ((16**32 - 1)/16**32) * ((16**32 - 2)/16**32)
= 8.8162076e-39
There are:
16**32 = 3.4028237e+38
different strings.
Your method can avoid duplicates when it works as intended, but it also has an unimaginablly slim chance of falling into perpetuality and never terminating.
Upvotes: 1
Reputation: 1836
Since it is a base 16 numeral system there are 16 ** SecureRandom.hex.length
possible variations which is quite a lot.
Better use loop with an exit condition if you do not want to overflow your stack when the quantity of users will increase.
MAX_ATTEMPTS = 3 # For you to choose.
def key
MAX_ATTEMPTS.times do
hex = SecureRandom.hex
return hex unless Model.where(key: hex).exists?
end
fail 'No attempts to generate a key left.'
end
Upvotes: 2