Categorygithub.com/solutionroute/rid
modulepackage
0.0.2
Repository: https://github.com/solutionroute/rid.git
Documentation: pkg.go.dev

# README

godocTestGo CoverageLicense: MIT

rid

Package rid provides a k-sortable, zero-configuration, unique ID generator. Binary IDs are Base32-encoded, producing a 24-character case-insensitive URL-friendly representation like: 062ekgz5k5f23ejagw2n7c9f. Helper functions for Base64 encoding and decoding are included.

Base32 encoding evenly aligns with 15 byte / 120 bit binary data. The 15-byte binary representation of an ID is comprised of a:

  • 6-byte timestamp value representing milliseconds since the Unix epoch
  • 1-byte machine+process signature, derived from md5(machine ID + process ID)
  • 8-byte random number using Go's runtime fastrand function. [1]

rid also implements a number of well-known interfaces to make use with JSON and databases more convenient.

Acknowledgement: This package borrows heavily from the at-scale capable rs/xid package which itself levers ideas from MongoDB.

Where this package differs, rid (15 bytes) | xid (12 bytes):

  • 6-bytes of time, millisecond resolution | 4 bytes, second resolution
  • 1-byte machine+process signature | 3 bytes machine ID, 2 bytes process ID
  • 8-byte random number | 3-byte monotonic counter randomly initialized once

Usage

	i := rid.New()
	fmt.Printf("%s\n", i)           // 062ekkxhmp31522vfjt7jv9t 

Batteries included

rid.ID implements a number of common interfaces including:

  • database/sql: driver.Valuer, sql.Scanner
  • encoding: TextMarshaler, TextUnmarshaler
  • encoding/json: json.Marshaler, json.Unmarshaler
  • Stringer

Package rid also provides a command line tool rid allowing for id generation and inspection. To install: go install github.com/solutionroute/rid/cmd/...

$ rid 
062ekjasgt18j0xgabq5zw45

$ rid -c 2
062ekjdxbc4yr0v0zyhv19zb
062ekjdxbc4pesrn45jfz89k

$ rid -c 2 -a  # use the alternate Base64 encoding:
AYTuBDyA4ZXGYHMV7E1s
AYTuBDyA4fh1zG-WbkUm

# produce 4 and inspect
$ rid `rid -c 4`
062ey7h3893fhrevfw2sm8qd ts:1670459040578 sig:0x46 rnd:17933856529767342829 2022-12-07 16:24:00.578 -0800 PST ID{0x1,0x84,0xef,0x1e,0x23,0x42,0x46,0xf8,0xe1,0xdb,0x7f,0x5,0x9a,0x22,0xed}
062ey7h38930ndcf996kt66m ts:1670459040578 sig:0x46 rnd:  771680460450109652 2022-12-07 16:24:00.578 -0800 PST ID{0x1,0x84,0xef,0x1e,0x23,0x42,0x46,0xa,0xb5,0x8f,0x4a,0x4d,0x3d,0x18,0xd4}
062ey7h3893frsw94a3mzkam ts:1670459040578 sig:0x46 rnd:18187656401551084884 2022-12-07 16:24:00.578 -0800 PST ID{0x1,0x84,0xef,0x1e,0x23,0x42,0x46,0xfc,0x67,0x89,0x22,0x87,0x4f,0xcd,0x54}
062ey7h38935krfsvfbcb2jw ts:1670459040578 sig:0x46 rnd: 6476732461731908188 2022-12-07 16:24:00.578 -0800 PST ID{0x1,0x84,0xef,0x1e,0x23,0x42,0x46,0x59,0xe1,0xf9,0xdb,0xd6,0xc5,0x8a,0x5c}

Random Source

For random number generation rid uses a Go runtime fastrand64 [1], available in Go versions released post-spring 2022; it's non-deterministic, goroutine safe, and fast. For the purpose of this package, fastrand64 seems ideal.

Use of fastrand makes rid performant and scales well as cores/parallel processes are added. While more testing will be done, no ID collisions have been observed over numerous runs producing upwards of 300 million ID using single and multiple goroutines.

[1] For more information on fastrand (wyrand) see: https://github.com/wangyi-fudan/wyhash and Go's sources for runtime/stubs.go.

Package Comparisons

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

PackageBLenELenK-Sort0-CfgEncoded ID and NextMethodComponents
solutionroute/rid
Base32 (default)
1524truetrue062exq1nk13qcc2bpjdbjx3b
062exq1nk13g08et49tn4h3f
fastrand6 byte ts(ms) : 1 byte machine/pid signature : 8 byte random
solutionroute/rid
Base64 (optional)
1520truetrueAYTu3DWYR7jwhCW8ENzp
AYTu3DWYR1LQQgoG7P9B
fastrand6 byte ts(ms) : 1 byte machine/pid signature : 8 byte random
rs/xid1220truetruece8hrfop26gfn8r71uh0
ce8hrfop26gfn8r71uhg
counter4 byte ts(sec) : 2 byte mach ID : 2 byte pid : 3 byte monotonic counter
segmentio/ksuid2027truetrue2IbeTgUpIvLr9xpVIYoD4pCrrM4
2IbeTmvvxbvLclWz7eSvQrRRTd5
random4 byte ts(sec) : 16 byte random
google/uuid1636falsetruea1797151-5d1c-47c0-b92a-81af479372b2
2064a35e-f2f4-4580-bcda-15a651582b77
crypt/randv4: 16 bytes random with version & variant embedded
oklog/ulid1626truetrue01GKQDRDCRD1DC3DF73MP7H0S0
01GKQDRDCRWT49J9ZH681MNN4N
crypt/rand6 byte ts(ms) : 10 byte counter random init per ts(ms)
kjk/betterguid1720truetrue-NIir2LN11DRU0HsEjcA
-NIir2LN11DRU0HsEjcB
counter8 byte ts(ms) : 9 byte counter random init per ts(ms)

If you don't need the k-sortable randomness this and other packages provide, consider the well-tested and performant capable rs/xid package upon which rid is based. See https://github.com/rs/xid.

For a detailed comparison of various golang unique ID solutions, including rs/xid, see: https://blog.kowalczyk.info/article/JyRZ/generating-good-unique-ids-in-go.html

Package Benchmarks

A comparison with the above noted packages can be found in eval/bench/bench_test.go. Output:

Intel 4-core Dell Latitude 7420 laptop

$ go test -cpu 1,2,4,8 -benchmem  -run=^$   -bench  ^.*$ 
goos: linux
goarch: amd64
pkg: github.com/solutionroute/rid/eval/bench
cpu: 11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz
BenchmarkRid            	27007984	        41.90 ns/op	       0 B/op	       0 allocs/op
BenchmarkRid-2          	54439544	        22.19 ns/op	       0 B/op	       0 allocs/op
BenchmarkRid-4          	86903547	        13.66 ns/op	       0 B/op	       0 allocs/op
BenchmarkRid-8     	       132959510	         8.965 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid            	31221853	        37.28 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid-2          	35561181	        33.30 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid-4          	55113584	        27.53 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid-8          	71106020	        16.70 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid          	 3821538	       314.6 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid-2        	 3205950	       367.6 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid-4        	 3195728	       374.0 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid-8        	 3193402	       406.0 ns/op	       0 B/op	       0 allocs/op
BenchmarkGoogleUuid     	 3561132	       334.0 ns/op	      16 B/op	       1 allocs/op
BenchmarkGoogleUuid-2   	 4955325	       226.5 ns/op	      16 B/op	       1 allocs/op
BenchmarkGoogleUuid-4   	 7119134	       160.6 ns/op	      16 B/op	       1 allocs/op
BenchmarkGoogleUuid-8   	 7670070	       133.7 ns/op	      16 B/op	       1 allocs/op
BenchmarkUlid           	  157432	      7488 ns/op	    5440 B/op	       3 allocs/op
BenchmarkUlid-2         	  245198	      4758 ns/op	    5440 B/op	       3 allocs/op
BenchmarkUlid-4         	  389346	      3082 ns/op	    5440 B/op	       3 allocs/op
BenchmarkUlid-8         	  556402	      2108 ns/op	    5440 B/op	       3 allocs/op
BenchmarkBetterguid     	13866880	        81.79 ns/op	      24 B/op	       1 allocs/op
BenchmarkBetterguid-2   	10869040	       102.7 ns/op	      24 B/op	       1 allocs/op
BenchmarkBetterguid-4   	 8379374	       138.3 ns/op	      24 B/op	       1 allocs/op
BenchmarkBetterguid-8   	 6705165	       176.1 ns/op	      24 B/op	       1 allocs/op

AMD 8-core desktop

$ go test -cpu 1,2,4,8,16 -benchmem  -run=^$   -bench  ^.*$
goos: linux
goarch: amd64
pkg: github.com/solutionroute/rid/eval/bench
cpu: AMD Ryzen 7 3800X 8-Core Processor             
BenchmarkRid              	19931982	        59.28 ns/op	       0 B/op	       0 allocs/op
BenchmarkRid-2            	39499843	        29.90 ns/op	       0 B/op	       0 allocs/op
BenchmarkRid-4            	78571719	        15.08 ns/op	       0 B/op	       0 allocs/op
BenchmarkRid-8     	       154435864	         7.715 ns/op	       0 B/op	       0 allocs/op
BenchmarkRid-16    	       279988606	         4.317 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid              	22248019	        52.32 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid-2            	37339971	       100.9 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid-4            	22793754	        52.99 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid-8            	43813854	        33.44 ns/op	       0 B/op	       0 allocs/op
BenchmarkXid-16           	67285090	        16.89 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid            	 3252950	       362.7 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid-2          	 2198566	       783.1 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid-4          	 1443458	       832.5 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid-8          	 1441783	       838.8 ns/op	       0 B/op	       0 allocs/op
BenchmarkKsuid-16         	 1407332	       857.6 ns/op	       0 B/op	       0 allocs/op
BenchmarkGoogleUuid       	 2900432	       352.8 ns/op	      16 B/op	       1 allocs/op
BenchmarkGoogleUuid-2     	 4841989	       214.2 ns/op	      16 B/op	       1 allocs/op
BenchmarkGoogleUuid-4     	 9413534	       110.3 ns/op	      16 B/op	       1 allocs/op
BenchmarkGoogleUuid-8     	18598221	        58.70 ns/op	      16 B/op	       1 allocs/op
BenchmarkGoogleUuid-16    	29231677	        40.91 ns/op	      16 B/op	       1 allocs/op
BenchmarkUlid             	  146024	      7890 ns/op	    5440 B/op	       3 allocs/op
BenchmarkUlid-2           	  276771	      4396 ns/op	    5440 B/op	       3 allocs/op
BenchmarkUlid-4           	  516540	      2348 ns/op	    5440 B/op	       3 allocs/op
BenchmarkUlid-8           	  800305	      1476 ns/op	    5440 B/op	       3 allocs/op
BenchmarkUlid-16          	  764970	      1484 ns/op	    5440 B/op	       3 allocs/op
BenchmarkBetterguid       	14442830	        80.73 ns/op	      24 B/op	       1 allocs/op
BenchmarkBetterguid-2     	10107843	       147.2 ns/op	      24 B/op	       1 allocs/op
BenchmarkBetterguid-4     	 5394602	       260.7 ns/op	      24 B/op	       1 allocs/op
BenchmarkBetterguid-8     	 4140949	       296.6 ns/op	      24 B/op	       1 allocs/op
BenchmarkBetterguid-16    	 3173990	       379.9 ns/op	      24 B/op	       1 allocs/op

# Packages

No description provided by the author
No description provided by the author

# Functions

FromBytes copies []bytes into an ID value.
FromString decodes the supplied Base32 representation.
FromString64 returns an ID by decoding a Base64 representation of an ID.
New returns a new ID using the current time;.
NewWithTimestamp returns a new ID using the supplied timestamp.
NilID returns a zero value for `rid.ID`.
Sort sorts an array of IDs inplace.
Alternative Base64 encoding/decoding helpers String64 returns a Base64 encoded representation of ID as a string.

# Variables

No description provided by the author

# Type aliases

ID represents a unique identifier.