Skip to content

Instantly share code, notes, and snippets.

@takekazuomi
Created March 15, 2014 18:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takekazuomi/9571376 to your computer and use it in GitHub Desktop.
Save takekazuomi/9571376 to your computer and use it in GitHub Desktop.
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