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.
- public class IdGenerator
- {
- public const long Twepoch = 1288834974000L;
- // change from 5 to 3
- private const int WorkerIdBits = 3;
- // change from 5 to 2
- private const int DatacenterIdBits = 2;
- // change from 12 to 8
- private const int SequenceBits = 8;
- private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);
- private const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits);
- private const long SequenceMask = -1L ^ (-1L << SequenceBits);
- private const int WorkerIdShift = SequenceBits;
- private const int DatacenterIdShift = SequenceBits + WorkerIdBits;
- public const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
- private long _sequence = 0L;
- private long _lastTimestamp = -1L;
- public long WorkerId { get; protected set; }
- public long DatacenterId { get; protected set; }
- public long Sequence
- {
- get { return _sequence; }
- internal set { _sequence = value; }
- }
- public IdGenerator(long workerId, long datacenterId, long sequence = 0L)
- {
- if (workerId > MaxWorkerId || workerId < 0)
- {
- throw new ArgumentException($"worker Id must greater than or equal 0 and less than or equal {MaxWorkerId}");
- }
- if (datacenterId > MaxDatacenterId || datacenterId < 0)
- {
- throw new ArgumentException($"datacenter Id must greater than or equal 0 and less than or equal {MaxDatacenterId}");
- }
- WorkerId = workerId;
- DatacenterId = datacenterId;
- _sequence = sequence;
- }
- private readonly object _lock = new object();
- public long NextId()
- {
- lock (_lock)
- {
- var timestamp = TimeGen();
- if (timestamp < _lastTimestamp)
- {
- throw new Exception($"timestamp error");
- }
- if (_lastTimestamp == timestamp)
- {
- _sequence = (_sequence + 1) & SequenceMask;
- if (_sequence == 0)
- {
- timestamp = TilNextMillis(_lastTimestamp);
- }
- }
- else
- {
- _sequence = 0;
- }
- _lastTimestamp = timestamp;
- return ((timestamp - Twepoch) << TimestampLeftShift) | (DatacenterId << DatacenterIdShift) | (WorkerId << WorkerIdShift) | _sequence;
- }
- }
- private long TilNextMillis(long lastTimestamp)
- {
- var timestamp = TimeGen();
- while (timestamp <= lastTimestamp)
- {
- timestamp = TimeGen();
- }
- return timestamp;
- }
- private long TimeGen()
- {
- return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
- }
- }
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.
- static void Main(string[] args)
- {
- // keep the generator singleton
- var generator = new IdGenerator(0, 0);
- System.Threading.Tasks.Parallel.For(0, 20, x =>
- {
- Console.WriteLine(generator.NextId().ToString());
- });
- Console.WriteLine("Hello World!");
- Console.ReadKey();
- }
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!

Join the conversation! Your thoughts help the community grow.