-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
149 lines (119 loc) · 3.23 KB
/
main.go
File metadata and controls
149 lines (119 loc) · 3.23 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
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
package main
import (
"errors"
"fmt"
"runtime"
"sync"
"time"
)
/*
Twitter Snowflake implementation
*/
const (
workerIDBits = 5
dataCenterIDBits = 5
sequenceBits = 12
maxWorkerID = -1 ^ (-1 << workerIDBits) // 32 mask: 0000 0000 0001 1111
maxDataCenterID = -1 ^ (-1 << dataCenterIDBits) // 32 mask: 0000 0000 0001 1111
maxSequence = -1 ^ (-1 << sequenceBits) // 4096 mas 1111 1111 1111
timestampShift = sequenceBits + workerIDBits + dataCenterIDBits // 22
datacenterShift = sequenceBits + workerIDBits // 17
workerShift = sequenceBits // 12
customEpoch = 1735689600000 // time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli()
)
type Generator struct {
mt sync.Mutex
workerID int64
dataCenterID int64
sequence int64
lastTimestamp int64
}
func NewGenerator(workerID, dataCenterID int64) (*Generator, error) {
if workerID < 0 || workerID > maxWorkerID {
return nil, fmt.Errorf("worker ID must be between 0 and %d", maxWorkerID)
}
if dataCenterID < 0 || dataCenterID > maxDataCenterID {
return nil, fmt.Errorf("worker ID must be between 0 and %d", maxDataCenterID)
}
return &Generator{
workerID: workerID,
dataCenterID: dataCenterID,
}, nil
}
func (g *Generator) Generate() (uint64, error) {
g.mt.Lock()
defer g.mt.Unlock()
now := time.Now().UTC().UnixMilli()
if now < g.lastTimestamp {
return 0, errors.New("clock moved backwards, refusing to generate id")
}
if now == g.lastTimestamp {
g.sequence = (g.sequence + 1) & maxSequence
fmt.Printf("parallel generating ids %d\n", g.sequence)
if g.sequence == 0 {
fmt.Println("sequence overflow, wait for next millisecond")
for now <= g.lastTimestamp {
now = time.Now().UTC().UnixMilli()
}
}
} else {
g.sequence = 0
}
g.lastTimestamp = now
timestamp := now - customEpoch
return (uint64(timestamp) << timestampShift) |
(uint64(g.dataCenterID) << datacenterShift) |
(uint64(g.workerID) << workerShift) |
uint64(g.sequence),
nil
}
func main() {
const (
genPerThread = 10_000
workerID int64 = 5
dataCenterID int64 = 9
)
generator, err := NewGenerator(workerID, dataCenterID)
if err != nil {
panic(err)
}
wg := sync.WaitGroup{}
for range runtime.NumCPU() {
wg.Add(1)
go func() {
defer wg.Done()
for range genPerThread {
id, err := generator.Generate()
if err != nil {
fmt.Println(fmt.Errorf("err id generating %w", err))
continue
}
fmt.Printf("ID: %d binary: %s\n", id, sPrintfBits(id))
}
}()
}
wg.Wait()
}
const (
ColorReset = "\033[0m"
ColorRed = "\033[31m"
ColorGreen = "\033[32m"
ColorYellow = "\033[33m"
ColorBlue = "\033[34m"
ColorPurple = "\033[35m"
)
func sPrintfBits(id uint64) string {
sequence := id & uint64(maxSequence)
workerID := (id >> workerShift) & uint64(maxWorkerID)
dataCenterID := (id >> datacenterShift) & uint64(maxDataCenterID)
timestamp := id >> timestampShift
signBit := id >> 63
return fmt.Sprintf(
"[%s%1b%s|%s%041b%s|%s%05b%s|%s%05b%s|%s%012b%s]",
ColorPurple, signBit, ColorReset,
ColorRed, timestamp, ColorReset,
ColorGreen, dataCenterID, ColorReset,
ColorYellow, workerID, ColorReset,
ColorBlue, sequence, ColorReset,
)
}