google / uuid

Go package for UUIDs based on RFC 4122 and DCE 1.1: Authentication and Security Services.
BSD 3-Clause "New" or "Revised" License
5.24k stars 363 forks source link

Panic when uuid.New().String() #97

Open nfoerster opened 2 years ago

nfoerster commented 2 years ago

I run a horizontally scaled application in k8s producing UUIDs for messages. However, rarely, some replicated pods restart due to a panic coming for the UUID library.

panic: unexpected EOF                                                                                                                                                                                                                        
goroutine 197286 [running]:                                                                                                                                                                                                                  
github.com/google/uuid.Must(...)                                                                                                                                                                                                             
 /home/vagrant/go/pkg/mod/github.com/google/uuid@v1.3.0/uuid.go:178                                                                                                                                                                      
github.com/google/uuid.New(...)                                                                                                                                                                                                              
 /home/vagrant/go/pkg/mod/github.com/google/uuid@v1.3.0/version4.go:14

Is there any OS/library causing these issues, like too little entropy? On my local machine, I run a test producing billions of UUIDs without any panic.

jensilo commented 1 month ago

Well, I can only guess why that happens. Too little entropy might be the problem, AFAIK crypto/rand (used by uuid.UUID) uses syscalls and should typically not create duplicates. The comments state:

// NewRandom returns a Random (Version 4) UUID. // // The strength of the UUIDs is based on the strength of the crypto/rand // package. // // Uses the randomness pool if it was enabled with EnableRandPool. // // A note about uniqueness derived from the UUID Wikipedia entry: // // Randomly generated UUIDs have 122 random bits. One's annual risk of being // hit by a meteorite is estimated to be one chance in 17 billion, that // means the probability is about 0.00000000006 (6 × 10−11), // equivalent to the odds of creating a few tens of trillions of UUIDs in a // year and having one duplicate.

Internally, the error happens as the io.Reader tries to read from either the rand pool of uuid or from the std rand.Reader. Since you haven't mentioned uuid.EnableRandPool I'm assuming you're not using it, meaning for UUID creation, uuid reads from this rand.Reader. Under the hood, at least for Linux and Unix-ish operating systems, it uses the getrandom, or getentropy syscalls or reads from /dev/urandom.

Too little entropy might lead to a sys-level error, e.g. for getrandom see EAGAIN.

However, I have two suggestions for you to try out:

  1. uuid.EnableRandPool, maybe it helps, comments state:

    // EnableRandPool enables internal randomness pool used for Random // (Version 4) UUID generation. The pool contains random bytes read from // the random number generator on demand in batches. Enabling the pool // may improve the UUID generation throughput significantly. // // Since the pool is stored on the Go heap, this feature may be a bad fit // for security sensitive applications.

  2. Otherwise, is it an option to not use uuid.New or uuid.NewString but instead uuid.NewRandom and re-try upon error? Because uuid.New is equivalent to uuid.Must(uuid.NewRandom).