Skip to content

Commit f853d80

Browse files
smolaRoberto Santalla
authored andcommitted
fix NewULID for systems with low time resolution (#270)
NewULID uses a pool of rand.Source using time.Now() as seed. In some systems with high concurrency and lower time resolution, this leads to multiple sources with the same sead, making NewULID return equal values on subsequent calls. This commit changes the seed to add a random number from the global shared rand.Source (which is thread-safe). Signed-off-by: Santiago M. Mola <[email protected]>
1 parent cf0ccd9 commit f853d80

File tree

2 files changed

+30
-10
lines changed

2 files changed

+30
-10
lines changed

model.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,8 @@ type Record interface {
229229

230230
var randPool = &sync.Pool{
231231
New: func() interface{} {
232-
return rand.NewSource(time.Now().UnixNano())
232+
seed := time.Now().UnixNano() + rand.Int63()
233+
return rand.NewSource(seed)
233234
},
234235
}
235236

model_test.go

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package kallax
22

33
import (
4+
"sync"
45
"testing"
56

67
"github.com/stretchr/testify/require"
@@ -23,16 +24,34 @@ func TestULID_Value(t *testing.T) {
2324

2425
func TestUULID_ThreeNewIDsAreDifferent(t *testing.T) {
2526
r := require.New(t)
26-
id1 := NewULID()
27-
id2 := NewULID()
28-
id3 := NewULID()
2927

30-
r.NotEqual(id1, id2)
31-
r.NotEqual(id1, id3)
32-
r.NotEqual(id2, id3)
33-
34-
r.True(id1 == id1)
35-
r.False(id1 == id2)
28+
goroutines := 100
29+
ids_per_goroutine := 1000
30+
31+
ids := make(map[ULID]bool, ids_per_goroutine*goroutines)
32+
m := &sync.Mutex{}
33+
34+
wg := &sync.WaitGroup{}
35+
wg.Add(goroutines)
36+
for i := 0; i < goroutines; i++ {
37+
go func() {
38+
var oids []ULID
39+
for j := 0; j < ids_per_goroutine; j++ {
40+
oids = append(oids, NewULID())
41+
}
42+
43+
m.Lock()
44+
for _, id := range oids {
45+
ids[id] = true
46+
}
47+
m.Unlock()
48+
wg.Done()
49+
}()
50+
}
51+
52+
wg.Wait()
53+
54+
r.Equal(goroutines*ids_per_goroutine, len(ids))
3655
}
3756

3857
func TestULID_ScanValue(t *testing.T) {

0 commit comments

Comments
 (0)