-
Notifications
You must be signed in to change notification settings - Fork 374
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Monotonicity in UUIDv7 #150
Changes from 5 commits
960071a
f27ed75
654bc6c
64f9de4
5986a71
4b1aab9
314e206
8888abc
a6e034a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -825,7 +825,7 @@ func TestVersion6(t *testing.T) { | |
func TestVersion7(t *testing.T) { | ||
SetRand(nil) | ||
m := make(map[string]bool) | ||
for x := 1; x < 32; x++ { | ||
for x := 1; x < 128; x++ { | ||
uuid, err := NewV7() | ||
if err != nil { | ||
t.Fatalf("could not create UUID: %v", err) | ||
|
@@ -874,20 +874,30 @@ func TestVersion7_pooled(t *testing.T) { | |
func TestVersion7FromReader(t *testing.T) { | ||
myString := "8059ddhdle77cb52" | ||
r := bytes.NewReader([]byte(myString)) | ||
r2 := bytes.NewReader([]byte(myString)) | ||
uuid1, err := NewV7FromReader(r) | ||
_, err := NewV7FromReader(r) | ||
if err != nil { | ||
t.Errorf("failed generating UUID from a reader") | ||
} | ||
_, err = NewV7FromReader(r) | ||
if err == nil { | ||
t.Errorf("expecting an error as reader has no more bytes. Got uuid. NewV7FromReader may not be using the provided reader") | ||
} | ||
uuid3, err := NewV7FromReader(r2) | ||
if err != nil { | ||
t.Errorf("failed generating UUID from a reader") | ||
} | ||
|
||
func TestVersion7Monotonicity(t *testing.T) { | ||
length := 1000 | ||
|
||
uuids := make([]string, length) | ||
for i := 0; i < length; i++ { | ||
uuidString, _ := NewV7() | ||
uuids[i] = uuidString.String() | ||
time.Sleep(time.Millisecond) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This forces this single test to take a full second. A better test might be to force timeNow to return the same time on every call. You don't need to store all the UUIDs, just the last one:
You could also run this test again where timeNow returns time incrementing by say 100 nanoseconds per call and if you want to go crazy, where timeNow returns time going backwards. |
||
//time.Sleep(time.Millisecond / 50) | ||
} | ||
if uuid1 != uuid3 { | ||
t.Errorf("expected duplicates, got %q and %q", uuid1, uuid3) | ||
|
||
for i := 1; i < len(uuids); i++ { | ||
if uuids[i-1] >= uuids[i] { | ||
t.Errorf("unexpected seq got %s >= %s", uuids[i-1], uuids[i]) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,15 +44,15 @@ func NewV7FromReader(r io.Reader) (UUID, error) { | |
|
||
// makeV7 fill 48 bits time (uuid[0] - uuid[5]), set version b0111 (uuid[6]) | ||
// uuid[8] already has the right version number (Variant is 10) | ||
// see function NewV7 and NewV7FromReader | ||
// see function NewV7 and NewV7FromReader | ||
func makeV7(uuid []byte) { | ||
/* | ||
0 1 2 3 | ||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| unix_ts_ms | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| unix_ts_ms | ver | rand_a | | ||
| unix_ts_ms | ver | rand_a (12 bit seq) | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
|var| rand_b | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
|
@@ -61,7 +61,8 @@ func makeV7(uuid []byte) { | |
*/ | ||
_ = uuid[15] // bounds check | ||
|
||
t := timeNow().UnixMilli() | ||
now := timeNow().UnixMicro() | ||
t, s := now/1000, now&4095 // 2^12-1, 12 bits | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so this can still return a UUID that is less than the previous UUID (two calls within the same microsecond). The only way to guarantee an always increasing UUID is to remember the last time/sequence returned. Perhaps something like:
|
||
|
||
uuid[0] = byte(t >> 40) | ||
uuid[1] = byte(t >> 32) | ||
|
@@ -70,6 +71,6 @@ func makeV7(uuid []byte) { | |
uuid[4] = byte(t >> 8) | ||
uuid[5] = byte(t) | ||
|
||
uuid[6] = 0x70 | (uuid[6] & 0x0F) | ||
// uuid[8] has already has right version | ||
uuid[6] = 0x70 | (0x0F & byte(s>>8)) | ||
uuid[7] = byte(s) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The period was lost here. In general, do not reformat comments unless they are incorrect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed