2017-03-12 19:37:53 +00:00
|
|
|
package snowflakes
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
counterLen = 10
|
|
|
|
counterMask = -1 ^ (-1 << counterLen)
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
errNoFuture = errors.New("Start Time cannot be set in the future")
|
|
|
|
)
|
|
|
|
|
|
|
|
// Generator is a fountain for new snowflakes. StartTime must be
|
|
|
|
// initialized to a past point in time and Instance ID can be any
|
|
|
|
// positive value or 0.
|
|
|
|
//
|
|
|
|
// If any value is not correctly set, new IDs cannot be produced.
|
|
|
|
type Generator struct {
|
|
|
|
StartTime int64
|
|
|
|
mutex *sync.Mutex
|
|
|
|
sequence int32
|
|
|
|
now int64
|
|
|
|
}
|
|
|
|
|
2017-03-13 17:45:23 +00:00
|
|
|
func (g *Generator) IDToUnix(id int) int {
|
|
|
|
return (id >> counterLen) + int(g.StartTime)
|
|
|
|
}
|
|
|
|
|
2017-03-12 19:37:53 +00:00
|
|
|
// NewID generates a new, unique snowflake value
|
|
|
|
//
|
|
|
|
// Up to 8192 snowflakes per second can be requested
|
|
|
|
// If exhausted, it blocks and sleeps until a new second
|
|
|
|
// of unix time starts.
|
|
|
|
//
|
|
|
|
// The return value is signed but always positive.
|
|
|
|
//
|
|
|
|
// Additionally, the return value is monotonic for a single
|
|
|
|
// instance and weakly monotonic for many instances.
|
|
|
|
func (g *Generator) NewID() (int64, error) {
|
|
|
|
if g.mutex == nil {
|
|
|
|
g.mutex = new(sync.Mutex)
|
|
|
|
}
|
|
|
|
if g.StartTime > time.Now().Unix() {
|
|
|
|
return 0, errNoFuture
|
|
|
|
}
|
|
|
|
g.mutex.Lock()
|
|
|
|
defer g.mutex.Unlock()
|
|
|
|
|
|
|
|
var (
|
|
|
|
now int64
|
|
|
|
flake int64
|
|
|
|
)
|
|
|
|
now = int64(time.Now().Unix())
|
|
|
|
|
|
|
|
if now == g.now {
|
|
|
|
g.sequence = (g.sequence + 1) & counterMask
|
|
|
|
if g.sequence == 0 {
|
|
|
|
for now <= g.now {
|
|
|
|
now = int64(time.Now().Unix())
|
|
|
|
time.Sleep(time.Microsecond * 100)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
g.sequence = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
g.now = now
|
|
|
|
|
|
|
|
flake = int64(
|
|
|
|
((now - g.StartTime) << counterLen) |
|
|
|
|
int64(g.sequence))
|
|
|
|
|
|
|
|
return flake, nil
|
|
|
|
}
|