-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathflake_test.go
More file actions
184 lines (158 loc) · 4.13 KB
/
flake_test.go
File metadata and controls
184 lines (158 loc) · 4.13 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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package goflake
import (
"sync"
"testing"
)
func TestNewGenerator(t *testing.T) {
t.Run("ValidNodeID", func(t *testing.T) {
_, err := NewGenerator(0)
if err != nil {
t.Errorf("expected no error for node ID 0, but got %v", err)
}
_, err = NewGenerator(maxNodeID)
if err != nil {
t.Errorf("expected no error for max node ID, but got %v", err)
}
})
t.Run("InvalidNodeID", func(t *testing.T) {
_, err := NewGenerator(-1)
if err == nil {
t.Error("expected an error for negative node ID, but got nil")
}
_, err = NewGenerator(maxNodeID + 1)
if err == nil {
t.Error("expected an error for oversized node ID, but got nil")
}
})
}
func TestNextIDUniqueness(t *testing.T) {
generator, err := NewGenerator(1)
if err != nil {
t.Fatalf("failed to create generator: %v", err)
}
const numIDs = 50000 // Generate a significant number of IDs
ids := make(map[ID]bool, numIDs)
for i := 0; i < numIDs; i++ {
id, err := generator.Next()
if err != nil {
t.Fatalf("failed to generate ID: %v", err)
}
if ids[id] {
t.Fatalf("duplicate ID generated: %d", id)
}
ids[id] = true
}
}
func TestNextIDMonotonicity(t *testing.T) {
generator, err := NewGenerator(1)
if err != nil {
t.Fatalf("failed to create generator: %v", err)
}
var lastID ID
for i := 0; i < 50000; i++ {
id, err := generator.Next()
if err != nil {
t.Fatalf("failed to generate ID: %v", err)
}
if i > 0 && id <= lastID {
t.Errorf("ID is not monotonic: current=%d, last=%d", id, lastID)
}
lastID = id
}
}
func TestIDDecomposition(t *testing.T) {
nodeID := int64(123)
generator, err := NewGenerator(nodeID)
if err != nil {
t.Fatalf("failed to create generator: %v", err)
}
id, err := generator.Next()
if err != nil {
t.Fatalf("failed to generate ID: %v", err)
}
if id.Node() != nodeID {
t.Errorf("expected node ID %d, but got %d", nodeID, id.Node())
}
// The increment should be 0 for the first ID in a new millisecond.
if id.Increment() != 0 {
t.Errorf("expected increment 0 for the first ID, but got %d", id.Increment())
}
// Check the timestamp. Allow for a small delta due to execution time.
expectedTime := (id.Int64() >> timeShift)
if id.Time() != expectedTime {
t.Errorf("expected time %d, but got %d", expectedTime, id.Time())
}
// Generate a second ID immediately to test the increment
id2, err := generator.Next()
if err != nil {
t.Fatalf("failed to generate second ID: %v", err)
}
// If they are in the same millisecond, the increment should be 1
if id2.Time() == id.Time() {
if id2.Increment() != 1 {
t.Errorf("expected increment to be 1, but got %d", id2.Increment())
}
}
}
func TestConcurrentGeneration(t *testing.T) {
generator, err := NewGenerator(1)
if err != nil {
t.Fatalf("failed to create generator: %v", err)
}
const numGoroutines = 50
const idsPerGoroutine = 2000
totalIDs := numGoroutines * idsPerGoroutine
var wg sync.WaitGroup
idChan := make(chan ID, totalIDs)
for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < idsPerGoroutine; j++ {
id, err := generator.Next()
if err != nil {
// Use t.Errorf from a goroutine as t.Fatalf will exit the wrong one
t.Errorf("failed to generate ID: %v", err)
return
}
idChan <- id
}
}()
}
wg.Wait()
close(idChan)
// Verify all collected IDs are unique
ids := make(map[ID]bool, totalIDs)
for id := range idChan {
if ids[id] {
t.Fatalf("found duplicate ID in concurrent generation: %d", id)
}
ids[id] = true
}
if len(ids) != totalIDs {
t.Errorf("expected %d unique IDs, but got %d", totalIDs, len(ids))
}
}
func BenchmarkSingleThread(b *testing.B) {
generator, err := NewGenerator(1)
if err != nil {
b.Fatalf("failed to create generator: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = generator.Next()
}
}
// BenchmarkConcurrent benchmarks ID generation from multiple goroutines.
func BenchmarkConcurrent(b *testing.B) {
generator, err := NewGenerator(1)
if err != nil {
b.Fatalf("failed to create generator: %v", err)
}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, _ = generator.Next()
}
})
}