Skip to content
/ rid Public

Package rid provides a performant, goroutine-safe generator of short k-sortable unique IDs suitable for situations where inter-process ID generation coordination is not required.

License

MIT, MIT licenses found

Licenses found

MIT
LICENSE
MIT
LICENSE.rs-xid
Notifications You must be signed in to change notification settings

mwyvr/rid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GitHub go.mod Go versiongodocTestLicense: MITCoverage

rid

Package rid provides a performant, goroutine-safe generator of short k-sortable unique IDs suitable for use where inter-process ID generation coordination is not required.

Using a non-standard character set (fewer vowels), IDs Base-32 encode as a 16-character URL-friendly, case-insensitive representation like dfp7qt0v2pwt0v2x.

An ID is a:

  • 4-byte timestamp value representing seconds since the Unix epoch, plus a
  • 6-byte random value; see the Random Source discussion.

Built-in (de)serialization simplifies interacting with SQL databases and JSON. cmd/rid provides the rid utility to generate or inspect IDs. Thanks to internal/fastrand introduced in Go 1.19 and made the default math/rand source in Go 1.20, ID generation starts fast and scales well as cores are added. De-serialization has also been optimized. See Package Benchmarks.

Why rid instead of alternatives?

  • At 10 bytes binary, 16 bytes Base32 encoded, rid.IDs are case-insensitive and short, yet with 48 bits of uniqueness per second, are unique enough for many use cases.
  • IDs have a random component rather than a potentially guessable monotonic counter found in some libraries.

Acknowledgement: This package borrows heavily from rs/xid (https://github.com/rs/xid), a zero-configuration globally-unique high-performance ID generator that leverages ideas from MongoDB (https://docs.mongodb.com/manual/reference/method/ObjectId/).

Example:

id := rid.New()
fmt.Printf("%s\n", id.String())
// Output: dfp7qt97menfv8ll

id2, err := rid.FromString("dfp7qt97menfv8ll")
if err != nil {
	fmt.Println(err)
}
fmt.Printf("%s %d %v\n", id2.Time(), id2.Random(), id2.Bytes())
// Output: 2022-12-28 09:24:57 -0800 PST 43582827111027 [99 172 123 233 39 163 106 237 162 115]

CLI

Package rid also provides the rid tool for id generation and inspection.

$ rid 
dfpb18y8dg90hc74

$ rid -c 2
dfp9l9cgs05blztq
dfp9l9d80yxdf804

# produce 4 and inspect
$ rid `rid -c 4`
dfp9lmz9ksw87w48 ts:1672255955 rnd:256798116540552 2022-12-28 11:32:35 -0800 PST ID{ 0x63, 0xac, 0x99, 0xd3, 0xe9, 0x8e, 0x78, 0x83, 0xf0, 0x88 }
dfp9lmxefym2ht2f ts:1672255955 rnd:190729433933902 2022-12-28 11:32:35 -0800 PST ID{ 0x63, 0xac, 0x99, 0xd3, 0xad, 0x77, 0xa8, 0x28, 0x68, 0x4e }
dfp9lmt5zjy7km9n ts:1672255955 rnd: 76951796109621 2022-12-28 11:32:35 -0800 PST ID{ 0x63, 0xac, 0x99, 0xd3, 0x45, 0xfc, 0xbc, 0x78, 0xd1, 0x35 }
dfp9lmxt5sms80m7 ts:1672255955 rnd:204708502569607 2022-12-28 11:32:35 -0800 PST ID{ 0x63, 0xac, 0x99, 0xd3, 0xba, 0x2e, 0x69, 0x94,  0x2, 0x87 }

Random Source

Since cryptographically secure IDs are not an objective for this package, other approaches could be considered. With Go 1.19, rid utilized an internal runtime fastrand64, providing single and multi-core performance benefits. Go 1.20 exposed fastrand64 via the stdlib. As of rid v1.1.6, the package depends on Go 1.22 math/rand/v2, which provides Uint64N().

You may also enjoy reading:

To satisfy whether rid.IDs are unique enough for your use case, run eval/uniqcheck/main.go with various values for number of go routines and iterations, or, at the command line, produce IDs and use OS utilities to check:

rid -c 2000000 | sort | uniq -d
// None output

Change Log

  • 2023-03-02 v1.1.6: Package depends on math/rand/v2 and now requires Go 1.22+.
  • 2023-01-23 Replaced the stdlib Base32 encoding/decoding with an unrolled version for decoding performance.
  • 2022-12-28 The "10byte" branch was merged to master; the "15byte-historical" branch will be left dormant.

Contributing

Contributions are welcome.

Package Comparisons

Comparison table generated by eval/compare/main.go:

Package BLen ELen K-Sort Encoded ID and Next Method Components
solutionroute/rid 10 16 true dqjllq1sr0lrb93k
dqjllq40n8t6yx3r
math/rand/v2 4 byte ts(sec) : 6 byte random
rs/xid 12 20 true cnijjn34l33778tp3ing
cnijjn34l33778tp3io0
counter 4 byte ts(sec) : 2 byte mach ID : 2 byte pid : 3 byte monotonic counter
segmentio/ksuid 20 27 true 2dCoMU7h8xaTlBhacYmys0CETTc
2dCoMQwFBYWoEi9mue6Kv1esiwy
math/rand 4 byte ts(sec) : 16 byte random
google/uuid 16 36 false c9c5e75e-1ec1-4718-99d9-024c84422e27
e6387602-e980-44d2-a295-8c444d815d52
crypt/rand v4: 16 bytes random with version & variant embedded
oklog/ulid 16 26 true 01HR3PM0YWWF2WK24EVAFHSTR8
01HR3PM0YWY56DH7AC0GX6YC4K
crypt/rand 6 byte ts(ms) : 10 byte counter random init per ts(ms)
kjk/betterguid 17 20 true -Ns6PVER-PgtrwP2IgwR
-Ns6PVER-PgtrwP2IgwS
counter 8 byte ts(ms) : 9 byte counter random init per ts(ms)

With only 48 bits of randomness per second, rid does not attempt to weigh in as a globally unique ID generator. If that is your requirement, rs/xid is a solid feature comparable alternative for such needs.

For a comparison of various Go-based unique ID solutions, see: https://blog.kowalczyk.info/article/JyRZ/generating-good-unique-ids-in-go.html

Package Benchmarks

A benchmark suite for the above-noted packages can be found in eval/bench/bench_test.go. All runs were done with scaling_governor set to performance:

echo "performance" | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
$ go test -cpu 1,2,4,8,16,32 -test.benchmem -bench .
goos: linux
goarch: amd64
pkg: github.com/mwyvr/rid/eval/bench
cpu: Intel(R) Core(TM) i9-14900K
BenchmarkRid              	39116605	        28.59 ns/op	       0 B/op	       0 allocs/op
BenchmarkRid-2            	70493946	        16.31 ns/op	       0 B/op	       0 allocs/op
BenchmarkRid-4            	82231498	        14.26 ns/op	       0 B/op	       0 allocs/op
BenchmarkRid-8            	75729330	        14.88 ns/op	       0 B/op	       0 allocs/op
BenchmarkRid-16           	59416164	        19.69 ns/op	       0 B/op	       0 allocs/op
BenchmarkRid-32           	68666065	        18.63 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid              	44036608	        27.24 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid-2            	39810664	        27.07 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid-4            	36596661	        30.20 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid-8            	36267940	        32.22 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid-16           	35589727	        31.99 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid-32           	56330694	        26.68 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid            	 5444830	       220.1 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid-2          	 4578231	       249.0 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid-4          	 4704010	       254.0 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid-8          	 4546252	       251.8 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid-16         	 4491115	       259.2 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid-32         	 4682730	       265.2 ns/op	       0 B/op	       0 allocs/op
BenchmarkGoogleUuid       	 6017610	       198.3 ns/op	      16 B/op	       1 allocs/op
BenchmarkGoogleUuid-2     	 9588854	       117.8 ns/op	      16 B/op	       1 allocs/op
BenchmarkGoogleUuid-4     	17966739	        67.01 ns/op	      16 B/op	       1 allocs/op
BenchmarkGoogleUuid-8     	26756277	        38.35 ns/op	      16 B/op	       1 allocs/op
BenchmarkGoogleUuid-16    	37318750	        37.57 ns/op	      16 B/op	       1 allocs/op
BenchmarkGoogleUuid-32    	39958574	        29.55 ns/op	      16 B/op	       1 allocs/op
BenchmarkUlid             	  201711	      5970 ns/op	    5440 B/op	       3 allocs/op
BenchmarkUlid-2           	  367596	      3084 ns/op	    5440 B/op	       3 allocs/op
BenchmarkUlid-4           	  636337	      1732 ns/op	    5440 B/op	       3 allocs/op
BenchmarkUlid-8           	 1000000	      1084 ns/op	    5440 B/op	       3 allocs/op
BenchmarkUlid-16          	 1000000	      1116 ns/op	    5440 B/op	       3 allocs/op
BenchmarkUlid-32          	 1000000	      1140 ns/op	    5440 B/op	       3 allocs/op
BenchmarkBetterguid       	22956550	        50.49 ns/op	      24 B/op	       1 allocs/op
BenchmarkBetterguid-2     	21297140	        51.48 ns/op	      24 B/op	       1 allocs/op
BenchmarkBetterguid-4     	15610149	        67.59 ns/op	      24 B/op	       1 allocs/op
BenchmarkBetterguid-8     	14513452	        80.38 ns/op	      24 B/op	       1 allocs/op
BenchmarkBetterguid-16    	11680210	       105.1 ns/op	      24 B/op	       1 allocs/op
BenchmarkBetterguid-32    	 9156219	       133.4 ns/op	      24 B/op	       1 allocs/op
PASS
ok  	github.com/mwyvr/rid/eval/bench	47.927s

About

Package rid provides a performant, goroutine-safe generator of short k-sortable unique IDs suitable for situations where inter-process ID generation coordination is not required.

Topics

Resources

License

MIT, MIT licenses found

Licenses found

MIT
LICENSE
MIT
LICENSE.rs-xid

Stars

Watchers

Forks

Languages