namespace MasstransferCommon.Utils; using System; public class SnowflakeId { private static readonly DateTime Epoch = new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc); private const int WorkerIdBits = 5; private const int DatacenterIdBits = 5; private const int SequenceBits = 12; private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits); private const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits); private const long WorkerIdShift = SequenceBits; private const long DatacenterIdShift = SequenceBits + WorkerIdBits; private const long TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; private const long SequenceMask = -1L ^ (-1L << SequenceBits); private readonly object _lock = new object(); private long _lastTimestamp = -1L; private long _sequence = 0L; private static readonly SnowflakeId Instance = new SnowflakeId(0, 0); public long WorkerId { get; } public long DatacenterId { get; } public SnowflakeId(long workerId, long datacenterId) { if (workerId > MaxWorkerId || workerId < 0) { throw new ArgumentException($"workerId must be between 0 and {MaxWorkerId}"); } if (datacenterId > MaxDatacenterId || datacenterId < 0) { throw new ArgumentException($"datacenterId must be between 0 and {MaxDatacenterId}"); } WorkerId = workerId; DatacenterId = datacenterId; } public static string GetNextId() { return Instance.NextId().ToString(); } private long NextId() { lock (_lock) { var timestamp = TimeGen(); if (timestamp < _lastTimestamp) { throw new InvalidOperationException("Clock moved backwards. Refusing to generate id"); } if (_lastTimestamp == timestamp) { _sequence = (_sequence + 1) & SequenceMask; if (_sequence == 0) { timestamp = TilNextMillis(_lastTimestamp); } } else { _sequence = 0L; } _lastTimestamp = timestamp; return ((timestamp - EpochTicks()) << (int)TimestampLeftShift) | (DatacenterId << (int)DatacenterIdShift) | (WorkerId << (int)WorkerIdShift) | _sequence; } } private long TilNextMillis(long lastTimestamp) { var timestamp = TimeGen(); while (timestamp <= lastTimestamp) { timestamp = TimeGen(); } return timestamp; } private long TimeGen() { return (long)(DateTime.UtcNow - Epoch).TotalMilliseconds; } private static long EpochTicks() { return (long)(Epoch - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds; } }