// simple generator for.. // Twitter Snowflake with custom definable epoch package flake import "encoding/base64" import "fmt" import "strconv" import "sync" import "time" const ( TimeBits = 41 NodeBits = 10 StepBits = 12 TimeMask int64 = -1 ^ (-1 << TimeBits) NodeMask int64 = -1 ^ (-1 << NodeBits) StepMask int64 = -1 ^ (-1 << StepBits) NodeMax = -1 ^ (-1 << NodeBits) TimeShift uint8 = NodeBits + StepBits NodeShift uint8 = StepBits ) type Node struct { sync.Mutex // TODO: find a way to avoid locks? // configurable values epoch int64 node int64 // runtime tracking values lastTime int64 step int64 } // Start a new Flake factory / server node using the given node number // sets with default settings, use helper functions to change // node, epoch, etc. func NewFlakeNode(node int64) (*Node, error) { if node < 0 || node > NodeMax { return nil, fmt.Errorf("Invalid node number.") } return &Node{ epoch: time.Date(2016, 1, 0, 0, 0, 0, 0, time.UTC).UnixNano() / int64(time.Millisecond), node: node, lastTime: 0, step: 0, }, nil } // high performance generator // well, that w as the idea... func (n *Node) Generator(c chan Flake) { ticker := time.NewTicker(time.Millisecond) now := int64(time.Now().UnixNano() / 1000000) for { n.step = 0 select { case c <- Flake((now-n.epoch)< now { return 0, fmt.Errorf("Invalid system time.") } if n.lastTime == now { n.step = (n.step + 1) & StepMask if n.step == 0 { for now <= n.lastTime { time.Sleep(100 * time.Microsecond) now = time.Now().UnixNano() / 1000000 } } } else { n.step = 0 } n.lastTime = now return Flake((now-n.epoch)< now { return 0, fmt.Errorf("Invalid system time.") } if n.lastTime == now { n.step = (n.step + 1) & StepMask if n.step == 0 { for now <= n.lastTime { time.Sleep(100 * time.Microsecond) now = time.Now().UnixNano() / 1000000 } } } else { n.step = 0 } n.lastTime = now return Flake((now-n.epoch)<> 22) + Epoch } func (f Flake) Node() int64 { return int64(f) & 0x00000000003FF000 >> 12 } func (f Flake) Sequence() int64 { return int64(f) & 0x0000000000000FFF }