Design Unique ID Generator in Distributed Systems

In this post I will not be solving a problem, instead I will be talking about something I learned by reading chapter 7 Design a Unique ID Generator in Distributed Systems in the book System Design Interview by Alex Wu.

The idea of a unique ID is a requirement in most distributed systems. In my case, some years ago I was developing a storage server and needed to name the objects of different types and sizes a client might wish to store and of course, at some later point in time, retrieve. The idea of a file system path did not make sense because it would be nearly impossible to keep the same immutable configuration among many distributed systems with a huge capacity.

If a client stored an object and a path in the file system, including machine name was returned, nothing could change with such a path during the lifetime of the object. A large portion of the objects we wished to store were permanent so that would add to the configuration of the disks.

Since capacity would grow into petabytes, and the capacity of disks is constantly increasing, we would have to reconfigure storage maintaining the same file paths. I am not sure if such an operation would be feasible. We could map the original file paths to newer ones in each system since with time, the hardware and logical configurations would change.

Many years ago I attended a DMIG (Data Management Interfaces Group) conference in Colorado. One of the speakers mentioned that in the future, a client anywhere in the world would present to this hypothetical system a unique object ID and the system would return the proper object. Such an idea was exactly what I had to architect, design, and implement for the distributed storage system I was working on.

In chapter 7 Alex Wu describes a design of a system to generate unique IDs. The design is based on something called Snowflake designed and implemented by Twitter in 2010. I picked the following reference in the book to the article Announcing Snowflake. If you are interested in the topic I suggest you spend a few minutes reading the entire article. It describes the problem, options, and solution (at least what Twitter came up with).

If you are interested in the Snowflake code by Twitter, you may find it in the GitHub repository at snowflake. There is a second version of the code at snowflake-2010. It is written in Scala and Apache Thrift.

Alex Wu describes the global ID on page 128 in his book. In a nutshell it contains the following:

Twitter Global ID

o zero bit          ( 1 bit)
o timestamp         (41 bits)
o datacenter ID     ( 5 bits)
o machine ID        ( 5 bits)
o sequence number   (12 bits)
        Total bits:  64 bits

The length of the global ID as required by Twitter, uses 64 bits which fits nicely in an unsigned long in the C / C++ programming language. I mention this because I used mainly C / C++ for my storage server implementation.

The sequence number used by Twitter contains 12 bits. Assuming that the clock tick in the timestamp has a granularity of 1 millisecond, a single server could generate up to 1’000 * 4’096 which is about 4M requests per second, which is well above the requirements stated in chapter 7 of the book.

In the storage server case, due to object longevity requirements, I decided to use 128 bits instead of 64.

Storage Server Global ID

o mac               ( 48 bits)
o disk drive ID     ( 32 bits)
o timeStamp         ( 32 bits)
o sequence number   ( 16 bits)
        Total bits:  128 bits

Initially the timestamp started the layout and the sequence number ended it. The `mac` was included but the `disc drive ID` was not. Instead I had a location ID. After multiple discussions with multiple potential customers, most of which had a copy of the software to experiment with, I made the decision to modify and rearrange the fields. The final layout worked for what I was looking for while keeping the system administrators at the client sites happy.

GUIDGenUnique <<<     sizeof(time_t): 4 line: 941
GUIDGenUnique <<< sizeof(__time32_t): 4 line: 942
GUIDGenUnique <<< sizeof(__time64_t): 8 line: 943
GUIDGenUnique <<<            mac: 0x14feb5f0be5a size:  48 (bits) line: 1037
GUIDGenUnique <<<    diskDriveID: 0x26e887b0     size:  32 (bits) line: 1082
GUIDGenUnique <<<      timeStamp: 0x61f00b56     size:  32 (bits) line: 1098
GUIDGenUnique <<< sequenceNumber: 0x00003aa5     size:  16 (bits) line: 1134
UtilityFunctions <<< GUID ==>14FEB5F0BE5A26E887B061F00B563AA5<==

This screen capture illustrates the generation of a GUID (global ID) used by the distributed storage server. It has stored billions of objects in thousands of instances.

  FROM [sdmsql].[dbo].[BITFILE_TBL]

(10 row(s) affected)

This is a query for some GUIDs (global unique IDs) on a test system. The storage servers use a distributed MS SQL Server or MySQL engine. Each instance only knows of the objects it stores locally. When a request comes to a storage node and it does not know about a GUID, it passes on the request to three peers. The first one that holds the object is used to satisfy the request. The process repeats if the object is not found. There is a TTL mechanism in built into the storage server to prevent loops. 

One more thing, I designed the distributed unique ID generator in the late-1990s.

If interested in distributed systems I recommend the book by Alex Wu. It covers several design interview questions and at the end of each chapter he has references to extend the material covered.

If you have comments or questions regarding this, or any other post in this blog, please do not hesitate and leave me a note below. I will reply as soon as possible.

Keep on reading and experimenting. It is one of the best ways to learn, become proficient, refresh your knowledge and enhance your developer / engineering toolset.

Thanks for reading, feel free to connect with me John Canessa at LinkedIn.



Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.