Generate A Snowflake ID Whose Length Is 16

Introduction

 
Snowflake is a service used to generate unique IDs for objects within Twitter (Tweets, Direct Messages, Users, Collections, Lists etc.). These IDs are unique 64-bit unsigned integers, which are based on time, instead of being sequential. The full ID is composed of a timestamp, a worker number, and a sequence number.
 
By default, 64-bit unsigned integers will generate an Id whose length is 19, but sometimes it may be too long, some customers need an Id whose length is 16.
 
In this article, I will show how can we adapt to generate an Id whose length is 16.
 

How to Do This?

 
The full ID is composed of a 41 bit timestamp, 10 bit worker number, and 12 bit sequence number.
 
We can reduce the bit count of those components to finish this work.
 
Here is a sample that we can follow.
  1. public class IdGenerator  
  2. {  
  3.     public const long Twepoch = 1288834974000L;  
  4.       
  5.     // change from 5 to 3  
  6.     private const int WorkerIdBits = 3;  
  7.   
  8.     // change from 5 to 2  
  9.     private const int DatacenterIdBits = 2;  
  10.   
  11.     // change from 12 to 8  
  12.     private const int SequenceBits = 8;  
  13.   
  14.     private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);  
  15.   
  16.     private const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits);  
  17.   
  18.     private const long SequenceMask = -1L ^ (-1L << SequenceBits);  
  19.   
  20.     private const int WorkerIdShift = SequenceBits;  
  21.   
  22.     private const int DatacenterIdShift = SequenceBits + WorkerIdBits;  
  23.   
  24.     public const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;  
  25.   
  26.     private long _sequence = 0L;  
  27.     private long _lastTimestamp = -1L;  
  28.   
  29.     public long WorkerId { getprotected set; }  
  30.   
  31.     public long DatacenterId { getprotected set; }  
  32.   
  33.     public long Sequence  
  34.     {  
  35.         get { return _sequence; }  
  36.         internal set { _sequence = value; }  
  37.     }  
  38.   
  39.     public IdGenerator(long workerId, long datacenterId, long sequence = 0L)  
  40.     {  
  41.         if (workerId > MaxWorkerId || workerId < 0)  
  42.         {  
  43.             throw new ArgumentException($"worker Id must greater than or equal 0 and less than or equal {MaxWorkerId}");  
  44.         }  
  45.   
  46.         if (datacenterId > MaxDatacenterId || datacenterId < 0)  
  47.         {  
  48.             throw new ArgumentException($"datacenter Id must greater than or equal 0 and less than or equal {MaxDatacenterId}");  
  49.         }  
  50.   
  51.         WorkerId = workerId;  
  52.         DatacenterId = datacenterId;  
  53.         _sequence = sequence;  
  54.     }  
  55.   
  56.     private readonly object _lock = new object();  
  57.   
  58.     public long NextId()  
  59.     {  
  60.         lock (_lock)  
  61.         {  
  62.             var timestamp = TimeGen();  
  63.             if (timestamp < _lastTimestamp)  
  64.             {  
  65.                 throw new Exception($"timestamp error");  
  66.             }  
  67.   
  68.             if (_lastTimestamp == timestamp)  
  69.             {  
  70.                 _sequence = (_sequence + 1) & SequenceMask;  
  71.   
  72.                 if (_sequence == 0)  
  73.                 {  
  74.                     timestamp = TilNextMillis(_lastTimestamp);  
  75.                 }  
  76.             }  
  77.             else  
  78.             {  
  79.                 _sequence = 0;  
  80.             }  
  81.   
  82.             _lastTimestamp = timestamp;  
  83.             return ((timestamp - Twepoch) << TimestampLeftShift) | (DatacenterId << DatacenterIdShift) | (WorkerId << WorkerIdShift) | _sequence;  
  84.         }  
  85.     }  
  86.   
  87.     private long TilNextMillis(long lastTimestamp)  
  88.     {  
  89.         var timestamp = TimeGen();  
  90.         while (timestamp <= lastTimestamp)  
  91.         {  
  92.             timestamp = TimeGen();  
  93.         }  
  94.   
  95.         return timestamp;  
  96.     }  
  97.   
  98.     private long TimeGen()  
  99.     {  
  100.         return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();  
  101.     }  
  102. }  
As you can see, the above code reduces the bit count of worker number and sequence number.
 
The next step is to use this IdGenerator.
  1. static void Main(string[] args)  
  2. {  
  3.     // keep the generator singleton  
  4.     var generator = new IdGenerator(0, 0);  
  5.   
  6.     System.Threading.Tasks.Parallel.For(0, 20, x =>   
  7.     {  
  8.         Console.WriteLine(generator.NextId().ToString());  
  9.     });  
  10.   
  11.     Console.WriteLine("Hello World!");  
  12.     Console.ReadKey();  
  13. }  
Here is the result of it.
 
NOTE
We should keep the generator qas a singleton, it means that we should only create the generator once. If not, it may generate some duplicate Ids.
 

Summary

 
This article showed you a simple solution of how to generate a snowflake id whose length is 16.
 
By the way, you can adjust the bit count to adapt your work.
 
I hope this will help you!