-
Notifications
You must be signed in to change notification settings - Fork 0
/
snowflake.go
84 lines (75 loc) · 1.92 KB
/
snowflake.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package snowflake
import (
"log"
"runtime"
"sync"
"time"
)
const (
maxNode = 1023
maxSeq = 4095
timeShift = 22
nodeShift = 12
twitterEpoch int64 = 1288834974657
)
// Snowflake is a unique ID generation algorithm which opensourced by Twitter.
type Snowflake struct {
locker sync.Mutex
epoch int64 // the epoch we used
lastTime time.Time
lastTimestamp int64 // timestamp in milliseconds, 41 bits
node uint16 // NodeID, 10 bits
sequence uint16 // sequence, 12 bits
}
// NewSnowflake creates and initializes a new Snowflake instance.
func NewSnowflake(node uint16) *Snowflake {
return NewSnowflakeEpoch(node, twitterEpoch)
}
// NewSnowflakeEpoch creates and initializes a new Snowflake instance.
func NewSnowflakeEpoch(node uint16, epoch int64) *Snowflake {
if node > maxNode {
panic("node should between 0 - 1023")
}
s := &Snowflake{
epoch: epoch,
lastTime: time.Now(),
node: node,
}
tInMS := s.lastTime.UnixNano() / 1000000 // in milliseconds
if tInMS < s.epoch {
panic("Time before Epoch")
}
s.lastTimestamp = tInMS - s.epoch
return s
}
// Next generates a unique 64-bit integer.
func (s *Snowflake) Next() int64 {
s.locker.Lock()
defer s.locker.Unlock()
var waitingForLifeToGetBackToNormal bool
for {
t, ts := s.ts()
if ts < s.lastTimestamp {
if !waitingForLifeToGetBackToNormal {
waitingForLifeToGetBackToNormal = true
log.Printf("snowflake: time moved backwards: %v ms", s.lastTimestamp-ts)
}
time.Sleep(time.Millisecond)
continue
}
waitingForLifeToGetBackToNormal = false
if ts > s.lastTimestamp {
s.lastTime = t
s.lastTimestamp = ts
s.sequence = 0
}
if s.sequence < maxSeq {
ID := s.lastTimestamp<<timeShift | int64(s.node)<<nodeShift | int64(s.sequence)
s.sequence++
return ID
}
// Retry
runtime.Gosched()
}
// Never reached
}