I have been on the search for a solution to this problem for 5 years with MongoDB. I just watched all the new announcements and I got pretty pumped with all the awesome features, but nothing came close to solving this issue. I decided its time to put it here so maybe it can be put on your radar for future updates… Fingers crossed!
The problem: We work in a very competitive industry. We give out quotes via our software with a random 8 digit quote number. We do this so our competitors can’t figure out how many quotes we give per day by getting a new quote every 24 hours and seeing how many where issued. These IDs have to be unique, never used before.
The Challenge: Distribute a series of IDs in a Unique Random order in the most simple way possible.
There are 3 solutions to this problem in MongoDB. All three suck.
Solution 1:
Pre-insert every ID into the Quotes collection as tiny records {_id: 55555555}, {_id: 55555556}, {_id: 55555557}, etc. Have logic to pick a random number, select the first unused record. For example random number: 55555554. Run query: {"_id": {$gte: 55555554}, “name_field”: {"$exists": false}}. Once a record is found, update it with the new quote data.
This is not a good solution because 1. you have to pre-create 100000000 records. 2. each time you create a new quote you are doing an update in place where the size of the document gets much larger, so MongoDB has to move the document to the bottom of the collection on disk, which is really not good.
Solution 2:
Mine the new ID out of previously inserted quotes. You could use a do a series of DB queries to figure out a new ID by fetching a portion of data out of the collection and looking for an ID gap. You could fetch a range of 1000 records. Lets say 55555555-55556555 and count how many records are found. If you the answer is not 1000, then perform tons of queries figuring out what ID can be used.
There are different ways you can do this idea, but the gist is multiple successive full loop query calls. Super painful. At least you don’t have to insert 100000000 first… progress?
Solution 3:
Maintain 2 different collections. One that stores the remaining IDs. Like: {_id: 55555555}, {_id: 55555556}, {_id: 55555557}, etc. The other that stores the Quotes. Picking a random number like: 55555554, you can run a query like: {"_id": {$gte: 55555554}}, pick a new ID record. Use this ID to create a new record in the Quotes collection, then remove it from the IDs collection. You could use a transaction to make this a bit safer.
The problem with this is you are still pre-creating 100000000 records. If you do a big import into the Quotes collection from a csv or something, this IDs collection can fall out of date with the Quotes collection. And lastly you now have 2 collections to maintain. Every time you login to your DB you are reminded that you are a failure…
Sadly this is the solution we have been using for the last 5-6 years. It sucks, I am always on the look for a better idea / solution!
Please note, this is not just a MongoDB problem. Every DB software I know has this issue. Solution 3 is the winner for Relational DBs on the internet where this issue is discussed. I just keep hoping that MongoDB is going to pull though with a feature that can bail me out of my sadness.
Does anyone have any ideas? Thanks for your support and for taking the time to read this insanely long post. Maybe a new feature??