-
Notifications
You must be signed in to change notification settings - Fork 372
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 1 commit
960071a
f27ed75
654bc6c
64f9de4
5986a71
4b1aab9
314e206
8888abc
a6e034a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -874,39 +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") | ||
} | ||
if uuid1 == uuid3 { // Montonicity | ||
t.Errorf("expected duplicates, got %q and %q", uuid1, uuid3) | ||
} | ||
} | ||
|
||
func TestVersion7Monotonicity(t *testing.T) { | ||
length := 4097 // [0x000 - 0xfff] | ||
myString := "8059ddhdle77cb52" | ||
|
||
SetClockSequence(0) | ||
length := 1000 | ||
|
||
uuids := make([]string, length) | ||
for i := 0; i < length; i++ { | ||
uuidString, _ := NewV7FromReader(bytes.NewReader([]byte(myString))) | ||
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) | ||
} | ||
|
||
for i := 1; i < len(uuids); i++ { | ||
if uuids[i-1] > uuids[i] { | ||
t.Errorf("expected seq got %s > %s", uuids[i-1], 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 |
---|---|---|
|
@@ -20,7 +20,6 @@ import ( | |
// NewV7 returns a Version 7 UUID based on the current time(Unix Epoch). | ||
// Uses the randomness pool if it was enabled with EnableRandPool. | ||
// On error, NewV7 returns Nil and an error | ||
// Note: this implement only has 12 bit seq, maximum of 4096 uuids are generated in 1 milliseconds | ||
func NewV7() (UUID, error) { | ||
uuid, err := NewRandom() | ||
if err != nil { | ||
|
@@ -53,7 +52,7 @@ func makeV7(uuid []byte) { | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| unix_ts_ms | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| unix_ts_ms | ver |rand_a (12 bit counter)| | ||
| unix_ts_ms | ver | rand_a (12 bit seq) | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
|var| rand_b | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
|
@@ -62,7 +61,8 @@ func makeV7(uuid []byte) { | |
*/ | ||
_ = uuid[15] // bounds check | ||
|
||
t, s := getTimeV7() | ||
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) | ||
|
@@ -74,20 +74,3 @@ func makeV7(uuid []byte) { | |
uuid[6] = 0x70 | (0x0F & byte(s>>8)) | ||
uuid[7] = byte(s) | ||
} | ||
|
||
func getTimeV7() (int64, uint16) { | ||
|
||
defer timeMu.Unlock() | ||
timeMu.Lock() | ||
|
||
if clockSeq == 0 { | ||
setClockSequence(-1) | ||
} | ||
now := timeNow().UnixMilli() | ||
|
||
if now <= lasttimev7 { | ||
clockSeq = clockSeq + 1 | ||
} | ||
lasttimev7 = now | ||
return now, clockSeq | ||
} |
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