UUID v8
Go implementation of custom version of UUID v8 as proposed by BGP. Peabody & K. Davis in New UUID Formats.
UUID v8 is a open format, where just the version and variant bits are fixed. This custom implementation is similar to UUID v7 with two major changes:
-
The time granularity has been increased from 1 millisecond to 20 microseconds. With UUID v7 you can create a sortable UUID every ~250 ns, with this implementation of UUID v8 sorting will work up to a creation rate of ~5 ns. The UUID v7 will overflow in ~8900 years, this UUID v8 implementation will overflow in ~120 years.
-
The CSPRNG random number generation has been replaced for the xoshiro256++ algorithm, which is not cryptographically secure. Therefore, this implementation should NOT be used for generation of cryptographic keys. UUID v4 is still the best option for cryptographic key generation due to its 122 pseudo-random bits. However, for creating unique identifiers where security is not important, such as database identifiers, this scheme is more performant, sortable, and a higher bit variety than UUID v7 due to the incread time granularity as mentioned above.
The resulting UUID v8 can be generated in very little time with a higher bit variation between sequential UUIDs than a UUID v7 due to the increased time granularity, while maintaining the ability to lexicongraphically sort by order of creation.
For a standardized implementation of a lexicographically sortable UUID, see my UUID v7 package.
Usage
Generate a new unique identifier:
uniqueID := uuidv8.New()
Print the newly generated UUID v8 as a hyphen delimited string:
str := uniqueID.String()
This value can be used to store in a database or for sorting.
To only print the last 12 characters of the UUID v8:
shortStr := uniqueID.Short()
This can be useful for logging to easily identify different UUIDs and should offer enough randomness for many cases.
You can retrieve the creation time and the sequence number from a generated UUID v8:
creationTime := uniqueID.CreationTime()
sequenceNumber := uniqueID.SequenceNumber()
The creation time has a 20 microsecond time resolution. The sequence numbers differentiate between and order UUIDs which were created within one time step.
If you want to quickly check in which order UUIDs were created the following methods can be used:
isNewer := uniqueID.After(anotherUUID)
isOlder := uniqueID.Before(anotherUUID)
To validate if a UUID generated by another package is a valid UUID v8, according to this implementation with a 20 microsecond time granularity, the validate functions can be used:
isValidByteSlice := uuidv7.IsValid(sketchyUUID)
isValidString := uuidv7.IsValidString(sketchyUUIDString)
Benchmark
Compared to generating a UUID v4 with the commonly used UUID package by Google generating a UUID v8 with this package is aroung 5x faster. Compared to my UUID v7 package generating a UUID v8 is around 6x faster. The significant increase in speed is due to the use of the highly efficient xoshiro256++ algorithm instead of the crypto/rand package for random number generation.
BenchmarkUUIDv4-16 3970946 306.4 ns/op 16 B/op 1 allocs/op
BenchmarkUUIDv7-16 3561507 348.3 ns/op 0 B/op 0 allocs/op
BenchmarkUUIDv8-16 18841580 58.50 ns/op 0 B/op 0 allocs/op
All other methods are implemented identically to my UUID v7 package. They are allo highly optimized with zero allocations.
BenchmarkAfter-16 31425165 37.26 ns/op 0 B/op 0 allocs/op
BenchmarkBefore-16 31687480 37.94 ns/op 0 B/op 0 allocs/op
BenchmarkCreationTime-16 92899360 12.76 ns/op 0 B/op 0 allocs/op
BenchmarkSequenceNumber-16 946633234 1.272 ns/op 0 B/op 0 allocs/op
BenchmarkShort-16 354629456 3.308 ns/op 0 B/op 0 allocs/op
BenchmarkString-16 63411822 18.39 ns/op 0 B/op 0 allocs/op
BenchmarkIsValid-16 20531877 54.92 ns/op 0 B/op 0 allocs/op
BenchmarkIsValidString-16 7589988 156.6 ns/op 0 B/op 0 allocs/op