Don't say random when you mean unique!

I made a mistake the other day.  It seems that I used a random number when I really wanted a unique number.

Let me explain.

A service I wrote needed to send a message to an older process every 30 seconds or so.  To do this, it put a file in a particular folder and gave it an 18-digit filename.  Something like 541238740514861189.input.  The older process picked up this file, did what it needed to do, and put a corresponding response file back in the folder for my process.  Something like 541238740514861189.output.  My process picked up the response and continued doing its thing.  Fairly simple in theory.

I figured that seeing as this is a single-threaded process and it only runs every 30 seconds, seeding a random number with a time value would work.  I was even using the Ticks property of the DateTime which measures time in 10-millionths of a second.  It didn't occur to me at the time that even though there was only one thread, there might be multiple copies of this service running.  Being a server with a multicore processor, that means that it's possible for the same line of code to be executed at exactly the same time.

Still, it'd have to be phenomenal bad luck for the same line of code to execute in two processes within the same 10-millionth of a second, right? The odds would have to be miniscule!  Well, it turns out that while the Ticks property of the .Net DateTime might measure the number of 100 nanosecond blocks since the first of January 0001, that doesn't say anthing about its resolution.  The resolution of DateTime is not 100 nanoseconds, it's about 15ms.  Suddenly the chances aren't so slim.  There are now only 2,000 blocks of time in that 30 seconds.

So yes, it was very, very unlucky for two of these service to generate the same "random" filename, but it was possible.  It was stupid of me to use a random number when I really needed a number that was unique.  They're different things.  Solving the problem was as simple as making sure the first x characters of the filename represent the process Id.  That can't be shared between processes so there'll be no collisions.

Lesson learned.  Also, I should probably stop publishing my mistakes...

Damian Brady

I'm an Australian developer, speaker, and author specialising in DevOps, MLOps, developer process, and software architecture. I love Azure DevOps, GitHub Actions, and reducing process waste.

--