-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
snowid.go
162 lines (142 loc) · 3.48 KB
/
snowid.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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package snowid
import (
"errors"
"time"
)
const (
Uint10Mask = (uint64(1) << 10) - 1
Uint12Mask = (uint64(1) << 12) - 1
Uint41Mask = (uint64(1) << 41) - 1
Uint40Mask = (uint64(1) << 40) - 1
Uint40Bit = uint64(1) << 40
)
var (
ErrInvalidEpoch = errors.New("failed to create snowid.Generator: invalid Epoch")
ErrInvalidID = errors.New("failed to create snowid.Generator: invalid ID")
ErrStopped = errors.New("failed to retrieve ID: snowid.Generator stopped")
)
// Clock abstract the standard time package
type Clock interface {
Since(t time.Time) time.Duration
Sleep(d time.Duration)
}
type defaultClock struct{}
func (defaultClock) Since(t time.Time) time.Duration {
return time.Since(t)
}
func (defaultClock) Sleep(d time.Duration) {
time.Sleep(d)
}
func DefaultClock() Clock {
return defaultClock{}
}
// Options options for Generator
type Options struct {
// Clock custom implementation of clock, default to standard library
Clock Clock
// Epoch pre-defined zero time in Snowflake algorithm, required
Epoch time.Time
// Grain time grain of ID, default to millisecond, minimum to millisecond
Grain time.Duration
// ID unique unsigned integer indicate the ID of current Generator instance, maximum 10 bits wide, default to 0
ID uint64
// LeadingBit whether to fill leadingBit bit in ID, default to false
// If you are planning to use the ID in a string field, this will ensure the ID is always the same length
LeadingBit bool
}
// Generator the main interface
type Generator interface {
// Stop shutdown the instance, release all related resources
// can not stop twice, NewID() invocation will panic after stopped
Stop()
// Count returns the count of generated ids
Count() uint64
// NewID returns a new id
NewID() uint64
}
type generator struct {
chReq chan struct{}
chResp chan uint64
chStop chan struct{}
epoch time.Time
grain time.Duration
leadingBit bool
shiftedID uint64
count uint64
clock Clock
}
// New create a new instance of Generator
func New(opts Options) (Generator, error) {
if opts.Clock == nil {
opts.Clock = DefaultClock()
}
if opts.Epoch.IsZero() {
return nil, ErrInvalidEpoch
}
if opts.ID&Uint10Mask != opts.ID {
return nil, ErrInvalidID
}
if opts.Grain <= time.Millisecond {
opts.Grain = time.Millisecond
}
sf := &generator{
chReq: make(chan struct{}),
chResp: make(chan uint64),
chStop: make(chan struct{}),
epoch: opts.Epoch,
grain: opts.Grain,
leadingBit: opts.LeadingBit,
shiftedID: opts.ID << 12,
clock: opts.Clock,
}
go sf.run()
return sf, nil
}
func (sf *generator) Stop() {
close(sf.chStop)
}
func (sf *generator) run() {
var nowT, lastT, seqID uint64
for {
select {
case <-sf.chReq:
retry:
nowT = uint64(sf.clock.Since(sf.epoch) / sf.grain)
if nowT == lastT {
seqID = seqID + 1
if seqID > Uint12Mask {
sf.clock.Sleep(sf.grain)
goto retry
}
} else {
lastT = nowT
seqID = 0
}
sf.count++
if sf.leadingBit {
sf.chResp <- (((nowT & Uint40Mask) | Uint40Bit) << 22) | sf.shiftedID | seqID
} else {
sf.chResp <- ((nowT & Uint41Mask) << 22) | sf.shiftedID | seqID
}
case <-sf.chStop:
return
}
}
}
func (sf *generator) Count() uint64 {
return sf.count
}
func (sf *generator) NewID() uint64 {
select {
case sf.chReq <- struct{}{}:
select {
case v := <-sf.chResp:
return v
case <-sf.chStop:
panic(ErrStopped)
}
return <-sf.chResp
case <-sf.chStop:
panic(ErrStopped)
}
}