-
Notifications
You must be signed in to change notification settings - Fork 0
/
snowflake_id.go
132 lines (115 loc) · 4.11 KB
/
snowflake_id.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
package id
import (
"strconv"
"sync"
"time"
"github.com/benz9527/xboot/lib/infra"
)
// SnowFlake 64 bits
// 0-00000000_00000000_00000000_00000000_00000000_0-00000_00000-00000000_0000
// 1 bit, as symbol, it always set as 0
// 41 bits, the diff val between current ts and start ts
// The timestamp in high bits will be affected by clock skew (clock rollback).
// 5 bits, as datacenter id
// 5 bits, as machine id
// 12 bits, as internal sequence number, max value is 4096 (2 ^ 12)
const (
classicSnowflakeStartEpoch = int64(946659661000) // 2001-01-01 01:01:01 UTC+8
classicSnowflakeTsDiffBits = uint(41)
classicSnowflakeDIDBits = uint(5) // DataCenter ID
classicSnowflakeMIDBits = uint(5) // Machine ID
classicSnowflakeSequenceBits = uint(12)
classicSnowflakeMIDShiftLeft = classicSnowflakeSequenceBits
classicSnowflakeDIDShiftLeft = classicSnowflakeMIDShiftLeft + classicSnowflakeMIDBits
classicSnowflakeTsDiffShiftLeft = classicSnowflakeDIDShiftLeft + classicSnowflakeDIDBits // 22 bits
classicSnowflakeSequenceMax = int64(-1 ^ (-1 << classicSnowflakeSequenceBits))
classicSnowflakeMIDMax = int64(-1 ^ (-1 << classicSnowflakeMIDBits))
classicSnowflakeDIDMax = classicSnowflakeMIDMax
classicSnowflakeTsDiffMax = int64(-1 ^ (-1 << classicSnowflakeTsDiffBits))
)
type sfError string
func (e sfError) Error() string { return string(e) }
const (
errSFInvalidDataCenterID = sfError("[snowflake-id] data-center id invalid")
errSFInvalidMachineID = sfError("[snowflake-id] machine id invalid")
)
// The now function is easily to be affected by clock skew. Then
// the global and unique id is unstable.
// Deprecated: Please use the SnowFlakeID(datacenterID, machineID int64, now func() time.Time) (UUIDGen, error)
func StandardSnowFlakeID(dataCenterID, machineID int64, now func() time.Time) (Gen, error) {
if dataCenterID < 0 || dataCenterID > classicSnowflakeDIDMax {
return nil, infra.WrapErrorStackWithMessage(errSFInvalidDataCenterID, "dataCenterID: "+strconv.FormatInt(dataCenterID, 10)+
" (max: "+strconv.FormatInt(classicSnowflakeDIDMax, 10)+")")
}
if machineID < 0 || machineID > classicSnowflakeMIDMax {
return nil, infra.WrapErrorStackWithMessage(errSFInvalidMachineID, "machineID: "+strconv.FormatInt(machineID, 10)+
" (max: "+strconv.FormatInt(classicSnowflakeMIDMax, 10)+")")
}
var lock = sync.Mutex{}
lastTs, sequence := int64(0), int64(0)
return func() uint64 {
lock.Lock()
defer lock.Unlock()
now := now().UnixNano() / 1e6
if now != lastTs {
sequence = int64(0)
} else {
sequence = (sequence + 1) & classicSnowflakeSequenceMax
if sequence == 0 {
for now <= lastTs {
now = time.Now().UnixNano() / 1e6
}
}
}
diff := now - classicSnowflakeStartEpoch
if diff > classicSnowflakeTsDiffMax {
return 0
}
lastTs = now
id := (diff << classicSnowflakeTsDiffShiftLeft) |
(dataCenterID << classicSnowflakeDIDShiftLeft) |
(machineID << classicSnowflakeMIDShiftLeft) |
sequence
return uint64(id)
}, nil
}
func SnowFlakeID(dataCenterID, machineID int64, now func() time.Time) (UUIDGen, error) {
if dataCenterID < 0 || dataCenterID > classicSnowflakeDIDMax {
return nil, infra.WrapErrorStack(errSFInvalidDataCenterID)
}
if machineID < 0 || machineID > classicSnowflakeMIDMax {
return nil, infra.WrapErrorStack(errSFInvalidMachineID)
}
var lock = sync.Mutex{}
lastTs, sequence := int64(0), int64(0)
id := new(uuidDelegator)
id.number = func() uint64 {
lock.Lock()
defer lock.Unlock()
now := now().UnixNano() / 1e6
if now != lastTs {
sequence = int64(0)
} else {
sequence = (sequence + 1) & classicSnowflakeSequenceMax
if sequence == 0 {
for now <= lastTs {
now = time.Now().UnixNano() / 1e6
}
}
}
diff := now - classicSnowflakeStartEpoch
if diff > classicSnowflakeTsDiffMax {
return 0
}
lastTs = now
id := (diff << classicSnowflakeTsDiffShiftLeft) |
(dataCenterID << classicSnowflakeDIDShiftLeft) |
(machineID << classicSnowflakeMIDShiftLeft) |
sequence
return uint64(id)
}
id.str = func() string {
return strconv.FormatUint(id.number(), 10)
}
return id, nil
}