5

My application is a game where I need each user to be able to create a unique, natural number ID code that can be bijectively converted to a "short string" the same way a url-shortener works. The "short string" part is very important to the game.

I thought about creating a child node with an auto-id key that stores the natural number index & short string and another child node that contains the natural number as the key and the previous auto-id key.

I'm worried about having a race condition in the unlikely event that two users create a new ID at the same time. Ideally, I'd like to be able to increment the IDs starting at 1000 to keep the short strings very short.

Does anyone know a good solution to this type of problem using firebase-database?

I want to keep the "short strings" under 6 characters in length and only use numbers, capital letters and hyphens. (so a 34 character alphabet, ommitting 1s, ls, Os and 0s for clarity)

Maybe this is not possible and I will have to use a 50 character alphabet to generate a random number and add it to every key.


One idea was to check the current highest key and generate a random number to add to that, but there is still no guarantee two users won't get the same number.


So far the only other idea I have to possibly prevent a race-condition if two users attempt to generate a new key at the same time is keeping an internal list of online users and in the child nodes of each user, create the keys and having a delay before posting to the database....requiring the users to check with all other users' requested keys.

This last idea seems convoluted and prone to errors, needing code to check the list every time a user reconnects in case they lost their connection last time they were online.

  • It's easy enough to convert an id to a short string. Is it okay if user id 1001 gives you a string of "ABC123" and 1002 gets "ABC124"? Or do you want them to be "random-looking" so that 1002 might be "Z7QJ48"? – Jim Mischel Nov 02 '16 at 21:05
  • They don't have to be random-looking... I just want to be able to guarantee that no two ids are the same if two users create one at the same time. – Nicholas Aliabadi Nov 02 '16 at 22:21
  • 1
    It appears the answer is using a transaction. Firebase transactions prevent two users writing the same data at the same time. I'll post a more detailed answer once I create a solution to my problem. – Nicholas Aliabadi Nov 02 '16 at 23:14
  • The simplest way is probably just to randomly generate the ID and have security rules that prevent an ID from being written onto other than by its creator. Then in the client in the unlikely event of a collision, you just retry. – Michael Bleigh Nov 03 '16 at 02:55
  • See also http://stackoverflow.com/questions/25720109/saving-firebase-bandwidth-by-shortening-field-names/25729326#25729326 – Kato Feb 24 '17 at 16:26

2 Answers2

4

hashids is a small script to convert a number into a short string and back (see http://hashids.org/).

This way you can simply use an increasing counter, and translate these to short strings.

I use this in Firebase by having a central "counter" node, which can only be incremented by 1 (use security rules). A client can increment this node by one using a transaction. The resulting number will be guaranteed unique for that client. (Note that the code in a transaction block can fire multiple times, if two clients fire the transaction exactly at the same time. So you need to use the final number that the transaction creates.)

The number that is unique for that client can then be used with hashids to create a short string that is also guaranteed to be unique.

Note: this doesn't solve the "problem" that users can guess the next ID. The short strings can be translated back to a number if you have the salt that is used to create the string. Then the next short string can easily be generated by using the hashids function. So if you create the short strings in your client, the salt is also known in the client and thus can be extracted by users.

Of course, this might not be a problem depending on your case.

matijse
  • 56
  • 2
  • Thank you for hashid. That is a great piece of software which I will now be using in my application. It didn't answer my question, but it will save me from having to write the code for this! Cheers!! +1 – Nicholas Aliabadi Nov 13 '16 at 04:34
  • Firestore has a limit of 1 write per second, having a counter node does not scale very well. – E. Sun Mar 07 '20 at 20:36
0

So I finally came up with a solution for this problem.

I am also creating an Android version of the app, so will have to find the equivalent way to do this in Android.

What I did was used a transaction block to increment a counter. Every time the counter successfully increments, it runs a completion block which updates the corresponding child node.

The next problem occurred when the same user wanted to perform multiple increments at a time. (this doesn't happen very often, but it does happen)

The transaction blocks each get their own thread, but the completion blocks will queue up on the main thread, waiting for the transactions to all complete.

Using GCD I was able to give each increment operation its own thread by using a DispatchQueue. Now users can perform however many increments they need to and be guaranteed to have our unique HashId tracking code corresponding to their item.

I ended up getting some help on this in the Firebase Slack group....good place to go to find active discussions!

Cheers!