-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgid_generator.go
130 lines (109 loc) · 3.92 KB
/
gid_generator.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
package gid
import (
"errors"
"fmt"
"github.com/zxgangandy/gid/config"
"github.com/zxgangandy/gid/worker"
"sync"
"time"
)
// UidGenerator generator interface
type UidGenerator interface {
GetUID() int64
ParseUID(uid int64) string
}
// DefaultUidGenerator default uid generator
type DefaultUidGenerator struct {
bitsAllocator *BitsAllocator
config config.UidConfig
mutex sync.Mutex
workerId int64
lastSecond int64
sequence int64
}
// New create the default uid generator instance
func New(config config.UidConfig) *DefaultUidGenerator {
idAssigner := worker.NewWorkerIdAssigner(config)
allocator := NewBitsAllocator(config.GetTimeBits(), config.GetWorkerBits(), config.GetSeqBits())
var workerId int64
workerId = idAssigner.AssignWorkerId()
if workerId > allocator.maxWorkerId {
workerId = workerId % allocator.maxWorkerId
}
return &DefaultUidGenerator{
bitsAllocator: allocator,
config: config,
workerId: workerId,
sequence: 0,
}
}
// GetUID generate the unique id
func (g *DefaultUidGenerator) GetUID() int64 {
c := g.config
return g.nextId(c.GetEpochSeconds(), c.GetMaxBackwardSeconds(), c.EnableBackward())
}
// ParseUID parse the generated unique id then get the meta information
// +------+----------------------+----------------+-----------+
// | sign | delta seconds | worker node id | sequence |
// +------+----------------------+----------------+-----------+
// 1bit 30bits 7bits 13bits
func (g *DefaultUidGenerator) ParseUID(uid int64) string {
totalBits := (uint32)(TotalBits)
allocateTotalBits := g.bitsAllocator.allocateTotalBits
signBits := g.bitsAllocator.signBits
timestampBits := g.bitsAllocator.timestampBits
workerIdBits := g.bitsAllocator.workerIdBits
sequenceBits := g.bitsAllocator.sequenceBits
sequence := uint64(uid<<(totalBits-sequenceBits)) >> (totalBits - sequenceBits)
workerId := uint64(uid<<(timestampBits+signBits+totalBits-allocateTotalBits)) >> (totalBits - workerIdBits)
deltaSeconds := int64(uint64(uid) >> (workerIdBits + sequenceBits))
// format as string
return fmt.Sprintf(
"{\"uid\":\"%d\",\"timestamp\":\"%d\",\"workerId\":\"%d\",\"sequence\":\"%d\"}",
uid, g.config.GetEpochSeconds()+deltaSeconds, workerId, sequence,
)
}
func (g *DefaultUidGenerator) nextId(epochSeconds, maxBackwardSeconds int64, enableBackward bool) int64 {
g.mutex.Lock()
defer g.mutex.Unlock()
currentSecond := g.getCurrentSecond(epochSeconds)
if currentSecond < g.lastSecond {
refusedSeconds := g.lastSecond - currentSecond
if !enableBackward {
panic(errors.New(fmt.Sprintf("Clock moved backwards. Refusing for %d seconds", refusedSeconds)))
}
if refusedSeconds <= maxBackwardSeconds {
for currentSecond < g.lastSecond {
currentSecond = g.getCurrentSecond(epochSeconds)
}
} else {
panic("Clock moved backwards. Refused seconds bigger than max backward seconds")
}
} else if currentSecond == g.lastSecond { // At the same second, increase sequence
g.sequence = (g.sequence + 1) & g.bitsAllocator.maxSequence
// Exceed the max sequence, we wait the next second to generate uid
if g.sequence == 0 {
currentSecond = g.getNextSecond(g.lastSecond, epochSeconds)
}
} else {
// At the different second, sequence restart from zero
g.sequence = 0
}
g.lastSecond = currentSecond
// Allocate bits for UID
return g.bitsAllocator.allocate(currentSecond-epochSeconds, g.workerId, g.sequence)
}
func (g *DefaultUidGenerator) getCurrentSecond(epochSeconds int64) int64 {
currentSeconds := time.Now().Unix()
if currentSeconds-epochSeconds > g.bitsAllocator.maxDeltaSeconds {
panic("Timestamp bits is exhausted. Refusing UID generate.")
}
return currentSeconds
}
func (g *DefaultUidGenerator) getNextSecond(lastTimestamp, epochSeconds int64) int64 {
timestamp := g.getCurrentSecond(epochSeconds)
for timestamp <= lastTimestamp {
timestamp = g.getCurrentSecond(epochSeconds)
}
return timestamp
}