-
Notifications
You must be signed in to change notification settings - Fork 168
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
TestMonotonicSafe fails sometimes #44
Conversation
Interestingly, the following patch causes it to behave as expected. diff --git a/ulid_test.go b/ulid_test.go
index 91aba2b..6177396 100644
--- a/ulid_test.go
+++ b/ulid_test.go
@@ -584,7 +584,7 @@ func TestMonotonicOverflow(t *testing.T) {
func TestMonotonicSafe(t *testing.T) {
var (
- seed = time.Now().UnixNano()
+ seed = int64(12345)
src = rand.NewSource(seed)
entropy = rand.New(src)
monotonic = ulid.Monotonic(entropy, 0) |
fdd4535
to
4ae6d0e
Compare
More results:
|
I've updated the test case to range across a variety of seed and inc parameters. It fails more often when seed is larger, but I haven't figured out a pattern besides that. And, just to make sure it's clear, the following patch results in 100% success: diff --git a/ulid_test.go b/ulid_test.go
index 31c39af..f5f3fc4 100644
--- a/ulid_test.go
+++ b/ulid_test.go
@@ -612,12 +612,11 @@ func TestMonotonicSafe(t *testing.T) {
src = rand.NewSource(seed)
entropy = rand.New(src)
monotonic = ulid.Monotonic(entropy, inc)
- safe = &safeReader{r: monotonic}
)
t0 := time.Now()
- u0 := ulid.MustNew(ulid.Timestamp(t0), safe)
- u1 := ulid.MustNew(ulid.Timestamp(t0), safe)
+ u0 := ulid.MustNew(ulid.Timestamp(t0), monotonic)
+ u1 := ulid.MustNew(ulid.Timestamp(t0), monotonic)
if u0.String() >= u1.String() {
t.Fatalf("%s (time %d entropy %x) >= %s (time %d entropy %x)", u0.String(), u0.Time(), u0.Entropy(), u1.String(), u1.Time(), u1.Entropy()) |
@seebs has spotted the problem. ulid.New does a type assertion on the entropy io.Reader parameter, and checks explicitly for func New(ms uint64, entropy io.Reader) (id ULID, err error) {
if err = id.SetTime(ms); err != nil {
return id, err
}
switch e := entropy.(type) {
case nil:
return id, err
case *monotonic: // <--------
err = e.MonotonicRead(ms, id[6:])
default:
_, err = io.ReadFull(e, id[6:])
} Since I am passing a I am not sure the best way to patch this. |
I think that should work. @tsenart let me know if these changes to the package API surface area are acceptable to you. We could also offer SafeMonotonicReader in the package itself, if you think that would have value. |
var wg sync.WaitGroup | ||
for i := 0; i < 100; i++ { | ||
wg.Add(1) | ||
go func() { |
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.
SA2002: the goroutine calls T.Fatalf, which must be called in the same goroutine as the test (from staticcheck
)
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.
Whoa really
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.
yup! it's not reliably enforced, but it's there. you have to snag the error (maybe use an ErrGroup) and fatal out from the original goroutine.
ulid.Monotonic is not goroutine-safe, so I want to protect it with a mutex via e.g.
safeReader
. However, when I do that, it no longer reliably produces monotonically-increasing entropy. Here is the test output on my machine.