2

I'm developing a multi-process solution and have to assign 6 digit unique numbers to some objects (specifying more than 6 digits or using alphanumeric characters is not possible. The length of this number is a third-party constraint).

since this is a multi-process solution (Multiple instances of my application will be run at the same time), I cannot depend on using lock with shared variables in my project and I'd like to prevent using shared storage (db/file system/...) to maintain and generate these unique numbers.

Also, It's highly unlikely that I need more than 300,000 unique numbers per day.
Is there an algorithm (probably dependent on time of day) that can generate unique 6-digit numbers per day?

Kamyar
  • 18,639
  • 9
  • 97
  • 171
  • 1
    How many processes are there, how balanced is the traffic, and do you have *any* shared storage you could use (e.g. for hi/lo ID generation schemes)? – Jon Skeet Jun 02 '14 at 12:04
  • The last time I looked a day had under 100000 seconds (86400) so you can get 3 numbers per second. That should not be enough by itself to allow concurrency. So I don't think you can do without some sort of check, which obviously leads back to a counter, more or less. Maybe it is good enough to do hit and try..? – TaW Jun 02 '14 at 12:08
  • 1
    They have to be unique, not **random**, right? Then you can use Interlocked.Increment. – Dennis_E Jun 02 '14 at 12:08
  • 1
    @Dennis_E Interlocked would be fine if this was a single process, but the question specifically says it is multi-process. – Daniel Kelley Jun 02 '14 at 12:13
  • @JonSkeet: It'll be about 100-300 processes. I can make use of SQL Server as a shared storage. Although I prefer an independent approach if possible – Kamyar Jun 02 '14 at 12:19
  • @Dennis_E Yes. There's no need for randomness. But As Daniel Kelley pointed out, it's a multi process solution – Kamyar Jun 02 '14 at 12:20
  • 2
    Again, what's the level of *balance* involved? Can you guarantee that it will be distributed pretty evenly? If so, just give each process a start value and let it generate a portion of the space... you can't really *guarantee* uniqueness without having some sort of communication or other bounds. – Jon Skeet Jun 02 '14 at 12:23
  • @JonSkeet I see. Fair enough. Yes the distribution is almost balanced. Thought there might be some algorithms like GUID that can generate less unique values in time with less digits. – Kamyar Jun 02 '14 at 12:25

2 Answers2

4

Would it be possible to divide up groups of numbers to give to each thread? Ie. if you have a total of 300 000 id numbers and 3 threads, then you can provide each thread an interval to work with.

Thread 1 ->  1 - 99 9999 
Thread 2 ->  100 000 - 199 999 
Thread 3 ->  200 000 - 300 000

You could provide smaller intervals as well and a thread gets assigned a number of ids that it can use and when it runs out it has to go back and ask a central id organizer for a new set of ids to work with.

Would be something like

while(HasWorkTodo)
{
  foreach(int uniqueId in GetUniqueIDIntervalFromCentralIdController()) // get 10000 ids at a time
  {
    //work with globally unique id
  }
}

If the number of id's aren't too abundant I would also work out some way to hand back unused IDs to the main id controller (ie if your thread requests 1000 ids but only uses 50 and then finishes).

JensB
  • 6,663
  • 2
  • 55
  • 94
2

Regarding all the comments and info given, it seems the solution lies in the fact that each thread can have a 3 digit unique id. Then you can assign 1000 unique id per thread which is enough. Since you precised that:

  • "Yes the distribution is almost balanced"
  • "It's highly unlikely that I need more than 300,000 unique numbers per day"
  • "It'll be about 100-300 processes"

it is "highly unlikely" that you need more than 300 id per thread. Let's make it 1000 ("Yes we can!").

Then, in a given thread, use a single instance of the following class to generate Ids. No need for it to be static, just the same instance for the whole batch for a given thread

public class ThreadUniqueIdProvider
{
    private int ThreadId { get; set; }
    private int IdIndex { get; set; }

    public ThreadUniqueIdProvider(int threadId)
    {
        if (threadId < 0 || threadId > 999)
            throw new ArgumentException("Thread unique identifier must be in [0, 999]");

        this.ThreadId = threadId;
    }

    public bool HasReachedLimit
    {
        get
        {
            return this.IdIndex >= 999;
        }
    }

    public int NextId
    {
        get
        {
            if (this.HasReachedLimit)
                throw new Exception("Impossible to allocate more than 1000 Unique Ids per thread");

            this.IdIndex++;
            return this.ThreadId * 1000 + this.IdIndex;
        }
    }
}

And simply call NextIdwhen you need one.


Edit

According to comment, added HasReachedLimit in order to check if the thread can accept a new job.

Askolein
  • 3,250
  • 3
  • 28
  • 40
  • 1
    Rather than die unexpectedly when hitting the id limit for a thread, it would be better for threads to stop accepting work for the day when they are about to run out of ids. – Christopher Barber Jun 03 '14 at 00:21
  • @ChristopherBarber you are right. Added a simple `HasReachedLimit` to encourage such usage. – Askolein Jun 03 '14 at 07:23