Created
March 15, 2014 18:05
-
-
Save takekazuomi/9571376 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.CompilerServices; | |
namespace Snowflake001 | |
{ | |
public class IdWorker | |
{ | |
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | |
private const long TwEpoch = 1288834974657L; | |
private const int WorkerIdBits = 5; | |
private const int DatacenterIdBits = 5; | |
const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits); | |
const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits); | |
private const int SequenceBits = 12; | |
private const int WorkerIdShift = SequenceBits; | |
private const int DatacenterIdShift = SequenceBits + WorkerIdBits; | |
private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; | |
private const long SequenceMask = -1L ^ (-1L << SequenceBits); | |
private long _lastTimestamp = -1L; | |
private long _sequence; | |
private readonly long _workerId; | |
private readonly long _datacenterId; | |
public IdWorker(long workerId, long datacenterId, long sequence = 0L) | |
{ | |
// sanity check for workerId | |
if (workerId > MaxWorkerId || workerId < 0) | |
{ | |
throw new ArgumentOutOfRangeException(string.Format( | |
"worker Id can't be greater than {0} or less than 0", MaxWorkerId)); | |
} | |
if (datacenterId > MaxDatacenterId || datacenterId < 0) | |
{ | |
throw new ArgumentOutOfRangeException(string.Format( | |
"datacenter Id can't be greater than {0} or less than 0", MaxDatacenterId)); | |
} | |
_sequence = sequence; | |
_workerId = workerId; | |
_datacenterId = datacenterId; | |
} | |
public long GetId(string useragent) | |
{ | |
return NextId(); | |
} | |
[MethodImpl(MethodImplOptions.Synchronized)] | |
private long NextId() | |
{ | |
var timestamp = TimeGen(); | |
// check backwards clock. | |
if (timestamp < _lastTimestamp) | |
{ | |
// log.error("clock is moving backwards. Rejecting requests until %d.", lastTimestamp); | |
throw new InvalidOperationException( | |
string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds", | |
_lastTimestamp - timestamp)); // TODO InvalidSystemClock | |
} | |
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) // TODO full spin ? | |
{ | |
timestamp = TimeGen(); | |
} | |
return timestamp; | |
} | |
private long TimeGen() | |
{ | |
// Equivalent to a System.currentTimeMillis() in the Java | |
return (long) ((DateTime.UtcNow - UnixEpoch).TotalMilliseconds); // TODO check overflow | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment