-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnode.go
More file actions
100 lines (82 loc) · 1.87 KB
/
Copy pathnode.go
File metadata and controls
100 lines (82 loc) · 1.87 KB
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package snowflake
import (
"crypto/rand"
"encoding/binary"
"errors"
"sync"
"time"
)
var (
ErrNodeIDInvalid = errors.New("node ID must be between 0 and 1023")
ErrClockBackwards = errors.New("clock moved backwards")
)
type Node struct {
mu sync.Mutex
epoch int64
node int64
time int64
step int64
}
func New(id int64) (*Node, error) {
// If id is -1, a random 10-bit instance ID is generated
if id == -1 {
var byt [8]byte
_, _ = rand.Read(byt[:])
id = int64(binary.BigEndian.Uint64(byt[:]) & uint64(MaxNode))
}
if id < 0 || id > MaxNode {
return nil, ErrNodeIDInvalid
}
return &Node{
epoch: BaseEpoch,
node: id,
}, nil
}
func NewFromParts(datacenterID, workerID int64) (*Node, error) {
if datacenterID < 0 || datacenterID > MaxDatacenter {
return nil, errors.New("datacenter ID must be between 0 and 31")
}
if workerID < 0 || workerID > MaxWorker {
return nil, errors.New("worker ID must be between 0 and 31")
}
id := (datacenterID << WorkerBits) | workerID
return New(id)
}
func (n *Node) SetEpoch(epoch int64) {
n.mu.Lock()
defer n.mu.Unlock()
n.epoch = epoch
}
func (n *Node) Generate() (ID, error) {
n.mu.Lock()
defer n.mu.Unlock()
now := time.Now().UnixMilli()
if now < n.epoch {
return 0, errors.New("current time is before epoch")
}
if now < n.time {
// We wait for the clock to catch up if the drift is within 5ms
drift := n.time - now
if drift <= 5 {
time.Sleep(time.Duration(drift) * time.Millisecond)
now = time.Now().UnixMilli()
}
if now < n.time {
return 0, ErrClockBackwards
}
}
if now == n.time {
n.step = (n.step + 1) & MaxStep
if n.step == 0 {
// We increase the timestamp by 1ms to avoid sequence overflow
now = n.time + 1
}
} else if now > n.time {
n.step = 0
}
n.time = now
r := ID((now-n.epoch)<<TimestampShift |
(n.step << StepShift) |
(n.node << NodeShift))
return r, nil
}