kid

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 9, 2025 License: BSD-3-Clause, MIT Imports: 8 Imported by: 0

README

GitHub go.mod Go versiongodocTestCoverage

kid

Package kid (K-sortable ID) provides a goroutine-safe generator of short (10 byte binary, 16 bytes when base32 encoded), url-safe, k-sortable unique IDs.

The 10-byte binary representation of an ID is composed of:

  • 6-byte value representing Unix time in milliseconds
  • 2-byte sequence, and,
  • 2-byte random value.

IDs encode (base32) as 16-byte url-friendly strings that look like:

06bqj05bhh2lcbdb

kid.ID features

  • Size: 10 bytes as binary, 16 bytes if stored/transported as an encoded string.
  • Timestamp + sequence is guaranteed to be unique.
  • 2 bytes of trailing randomness to prevent simple counter attacks.
  • K-orderable in both binary and base32 encoded representations.
  • URL-friendly custom encoding without the vowels a, i, o, and u.
  • Automatic (un)/marshalling for SQL and JSON.
  • The cmd/kid tool for ID generation and introspection.

Example usage

func main() {
    id := kid.New()
	  fmt.Printf("%s %s %03v\n", id, id.String(), id[:])
	  // Example output: 06bq7xhnr03mlz6r 06bq7xhnr03mlz6r [001 149 115 246 021 192 007 073 252 216]

	  id, err := kid.FromString("06bq7xhnr03mlz6r")
	  if err != nil {
	  	// do something
	  }
	  fmt.Printf("%s %s %03v\n", id, id.String(), id[:])
	  // Output: 06bq7xhnr03mlz6r 06bq7xhnr03mlz6r [001 149 115 246 021 192 007 073 252 216]
}

Acknowledgments

While the ID payload differs greatly, the API and much of this package borrows heavily from https://github.com/rs/xid, a zero-configuration globally-unique ID generator. ID unique timestamp+sequence pairs are generated from the google/uuidV7 getV7Time() algorithm.

Uniqueness

Each call to kid.New() is guaranteed to return a unique ID with a timestamp+sequence greater than any previous call.

To satisfy whether kid.IDs are unique, run eval/uniqcheck/main.go:

$ go run eval/uniqcheck/main.go -count 2000000 -goroutines 20

Generating 2,000,000 IDs per 20 goroutines: Total keys: 40,000,000. Keys in last time tick: 1,380. Number of dupes: 0

Or, at the command line, produce IDs and use OS utilities to check (single-threaded):

$ kid -c 2000000 | sort | uniq -d
// None output

CLI

Package kid also provides a tool for id generation and inspection:

$ kid 
06bpwm8x107evvh9

$ kid -c 2
06bpwm3hkm371gz4
06bpwm3hkm3d5ezr

# produce 4 and inspect
kid $(kid -c 4)
06bpwlvhb86bypp7 ts:1741312454738 seq:3247 rnd:23239 2025-03-07 01:54:14.738 +0000 UTC ID{  0x1, 0x95, 0x6e, 0x4f, 0x70, 0x52,  0xc, 0xaf, 0x5a, 0xc7 }
06bpwlvhb86gcdw6 ts:1741312454738 seq:3317 rnd:45958 2025-03-07 01:54:14.738 +0000 UTC ID{  0x1, 0x95, 0x6e, 0x4f, 0x70, 0x52,  0xc, 0xf5, 0xb3, 0x86 }
06bpwlvhb86gkmks ts:1741312454738 seq:3320 rnd:53817 2025-03-07 01:54:14.738 +0000 UTC ID{  0x1, 0x95, 0x6e, 0x4f, 0x70, 0x52,  0xc, 0xf8, 0xd2, 0x39 }
06bpwlvhb86gmb73 ts:1741312454738 seq:3322 rnd:10467 2025-03-07 01:54:14.738 +0000 UTC ID{  0x1, 0x95, 0x6e, 0x4f, 0x70, 0x52,  0xc, 0xfa, 0x28, 0xe3 }

Change Log

  • 2025-03-08 v1.2.0 released. Requires Go 1.24+.
  • 2025-03-06 Forked rid in favour of kid for true k-sortability, requiring a new ID payload, now expected to remain static. Improved code coverage and documentation.

Contributing

Contributions are welcome.

Package Comparisons

kid was born out of a desire for a short, k-sortable unique ID where global uniqueness or inter-process ID generation coordination is not required.

A comparison of a variety of ID generators:

Package BLen ELen K-Sort Encoded ID and Next Method Components
mwyvr/kid 10 16 true 06bqhl9qdw2ltsc7
06bqhl9qdw2lx33b
06bqhl9qdw2lzwvx
06bqhl9qdw2m0307
crypto/rand 6 byte ts(millisecond) : 2 byte sequence : 2 byte random
rs/xid 12 20 true cv6e14dq9fa1afbuoev0
cv6e14dq9fa1afbuoevg
cv6e14dq9fa1afbuof00
cv6e14dq9fa1afbuof0g
globally 4 byte ts(sec) : 2 byte mach ID : 2 byte pid : 3 byte monotonic counter
segmentio/ksuid 20 27 true 2u3bD06RFqKmjRocArG0nYFNbi1
2u3bD4lwRryOVcUn0RVf3N9Dfp3
2u3bCygWvDhLgo6VSydaDZHXi6q
2u3bD3jOEtbm2ftvHLt9fECjRu1
math/rand 4 byte ts(sec) : 16 byte random
google/uuid V4 16 36 false 782f8fea-ec19-41c0-a459-cf4f795b3071
759e72fd-ef8f-4e01-86ad-261263b02e3a
f7e787ed-8c56-4819-a494-7107c59c033b
89b0956e-50cb-488e-9641-ae2fc5526c8b
crypt/rand UUID v4: 16 bytes random with version & variant embedded
google/uuid V7 16 36 true 0195784d-3767-7551-8e7c-3b584bcc78d0
0195784d-3767-7552-9429-7c1f9a06de03
0195784d-3767-7555-865e-b951a7df6853
0195784d-3767-7556-9fa3-d40367d02981
crypt/rand UUID v7: 16 bytes : 8 bytes time+sequence, version/variant, random
chilts/sid 16 23 true 1WfzjWveCSO-4x2g1m260I~
1WfzjWveC_x-2LqZ~YcsV7L
1WfzjWveChs-4K0ebZwbnSv
1WfzjWveCpf-5G57fDXwfEW
math/rand 8 byte ts(nanosecond) 8 byte random
matoous/go-nanoid/v2 21 21 false IzV_WtDKMeJXByVzCStWI
Mm_QfTYzATNAj0tjwJ3gD
7cx9ReEADqWWUEK27y11v
0TF6y6BsGapagy15dXxV4
crypto/rand 21 byte rand (adjustable)
sony/sonyflake 16 29 true GU2TMOJSGA2DSMRVGIZTKOBVHAYDE
GU2TMOJSGA2DSMRVGIZTMNJRGMZTQ
GU2TMOJSGA2DSMRVGIZTOMJWHA3TI
GU2TMOJSGA2DSMRVGIZTOOBSGQYTA
ts+counter 39 bit ts(10msec) 8 bit seq, 16 bit mach id
oklog/ulid 16 26 true 01JNW4TDV7ZFQKNKYYBRSQ3BYB
01JNW4TDV7CMDMJ0FB5N1XCN3G
01JNW4TDV7422RPDYADKD0DASE
01JNW4TDV78NHZHTKKY2AARERT
crypt/rand 6 byte ts(ms) : 10 byte counter random init per ts(ms)
kjk/betterguid 17 20 true -OKsIISbpc0n250oHsGf
-OKsIISbpc0n250oHsGg
-OKsIISbpc0n250oHsGh
-OKsIISbpc0n250oHsGi
counter 8 byte ts(ms) : 9 byte counter random init per ts(ms)

An article presenting various Go-based unique ID solutions can be found at: https://blog.kowalczyk.info/article/JyRZ/generating-good-unique-ids-in-go.html

Package Benchmarks

A benchmark suite comparing some of 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/kid/eval/bench
cpu: Intel(R) Core(TM) i9-14900K
BenchmarkKid                	23253198	       44.42 ns/op	      0 B/op	      0 allocs/op
BenchmarkKid-2              	24385503	       49.65 ns/op	      0 B/op	      0 allocs/op
BenchmarkKid-4              	14705680	       75.48 ns/op	      0 B/op	      0 allocs/op
BenchmarkKid-8              	12582271	       98.48 ns/op	      0 B/op	      0 allocs/op
BenchmarkKid-16             	10654134	      114.5 ns/op	      0 B/op	      0 allocs/op
BenchmarkKid-32             	8707262	      140.4 ns/op	      0 B/op	      0 allocs/op
BenchmarkXid                	40021941	       28.57 ns/op	      0 B/op	      0 allocs/op
BenchmarkXid-2              	38214714	       31.09 ns/op	      0 B/op	      0 allocs/op
BenchmarkXid-4              	37732369	       31.65 ns/op	      0 B/op	      0 allocs/op
BenchmarkXid-8              	37982810	       32.00 ns/op	      0 B/op	      0 allocs/op
BenchmarkXid-16             	37114318	       32.72 ns/op	      0 B/op	      0 allocs/op
BenchmarkXid-32             	52958653	       22.30 ns/op	      0 B/op	      0 allocs/op
BenchmarkKsuid              	15703082	       75.19 ns/op	      0 B/op	      0 allocs/op
BenchmarkKsuid-2            	13473422	       81.83 ns/op	      0 B/op	      0 allocs/op
BenchmarkKsuid-4            	11726649	      100.7 ns/op	      0 B/op	      0 allocs/op
BenchmarkKsuid-8            	10216989	      117.4 ns/op	      0 B/op	      0 allocs/op
BenchmarkKsuid-16           	8344321	      148.0 ns/op	      0 B/op	      0 allocs/op
BenchmarkKsuid-32           	6745603	      181.3 ns/op	      0 B/op	      0 allocs/op
BenchmarkGoogleUuid         	23282745	       48.18 ns/op	     16 B/op	      1 allocs/op
BenchmarkGoogleUuid-2       	32059802	       37.19 ns/op	     16 B/op	      1 allocs/op
BenchmarkGoogleUuid-4       	36299127	       32.08 ns/op	     16 B/op	      1 allocs/op
BenchmarkGoogleUuid-8       	38249354	       31.25 ns/op	     16 B/op	      1 allocs/op
BenchmarkGoogleUuid-16      	34422613	       34.56 ns/op	     16 B/op	      1 allocs/op
BenchmarkGoogleUuid-32      	42726945	       28.28 ns/op	     16 B/op	      1 allocs/op
BenchmarkGoogleUuidV7       	13948104	       84.37 ns/op	     16 B/op	      1 allocs/op
BenchmarkGoogleUuidV7-2     	13603436	       84.85 ns/op	     16 B/op	      1 allocs/op
BenchmarkGoogleUuidV7-4     	11828551	       95.73 ns/op	     16 B/op	      1 allocs/op
BenchmarkGoogleUuidV7-8     	11514358	      102.4 ns/op	     16 B/op	      1 allocs/op
BenchmarkGoogleUuidV7-16    	9943927	      121.7 ns/op	     16 B/op	      1 allocs/op
BenchmarkGoogleUuidV7-32    	8140674	      150.6 ns/op	     16 B/op	      1 allocs/op
BenchmarkUlid               	 201520	     5700 ns/op	   5440 B/op	      3 allocs/op
BenchmarkUlid-2             	 384513	     3085 ns/op	   5440 B/op	      3 allocs/op
BenchmarkUlid-4             	 719776	     1734 ns/op	   5440 B/op	      3 allocs/op
BenchmarkUlid-8             	1000000	     1068 ns/op	   5440 B/op	      3 allocs/op
BenchmarkUlid-16            	 951507	     1206 ns/op	   5440 B/op	      3 allocs/op
BenchmarkUlid-32            	 907669	     1273 ns/op	   5440 B/op	      3 allocs/op
BenchmarkBetterguid         	25408560	       46.06 ns/op	     24 B/op	      1 allocs/op
BenchmarkBetterguid-2       	20633276	       52.74 ns/op	     24 B/op	      1 allocs/op
BenchmarkBetterguid-4       	18814110	       63.86 ns/op	     24 B/op	      1 allocs/op
BenchmarkBetterguid-8       	15597657	       78.82 ns/op	     24 B/op	      1 allocs/op
BenchmarkBetterguid-16      	10815397	      105.5 ns/op	     24 B/op	      1 allocs/op
BenchmarkBetterguid-32      	9364880	      134.0 ns/op	     24 B/op	      1 allocs/op
PASS
ok  	github.com/mwyvr/kid/eval/bench	53.447s

Documentation

Overview

Package kid (K-sortable ID) provides a goroutine-safe generator of short (10 byte binary, 16 bytes when base32 encoded), url-safe, k-sortable unique IDs.

The 10-byte binary representation of an ID is composed of:

  • 6-byte value representing Unix time in milliseconds
  • 2-byte sequence, and,
  • 2-byte random value.

IDs encode (base32) as 16-byte url-friendly strings.

kid.ID features:

  • Size: 10 bytes as binary, 16 bytes if stored/transported as an encoded string.
  • Timestamp + sequence is guaranteed to be unique.
  • 2 bytes of trailing randomness
  • K-orderable in both binary and base32 encoded representations.
  • URL-friendly custom encoding without the vowels a, i, o, and u.
  • Automatic (un)/marshalling for SQL and JSON.
  • The cmd/kid tool for ID generation and introspection.

Example usage:

func main() {
    id := kid.New()
	  fmt.Printf("%s %s %03v\n", id, id.String(), id[:])
	  // Example output: 06bq7xhnr03mlz6r 06bq7xhnr03mlz6r [001 149 115 246 021 192 007 073 252 216]

	  id, err := kid.FromString("06bq7xhnr03mlz6r")
	  if err != nil {
	  	// do something
	  }
	  fmt.Printf("%s %s %03v\n", id, id.String(), id[:])
	  // Output: 06bq7xhnr03mlz6r 06bq7xhnr03mlz6r [001 149 115 246 021 192 007 073 252 216]
}

Acknowledgments:

While the ID payload differs greatly, the API and much of this package borrows heavily from https://github.com/rs/xid, a zero-configuration globally-unique ID generator. ID unique timestamp+sequence pairs are generated from the google/uuidV7 getV7Time() algorithm.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (

	// ErrInvalidID represents an error state, typically when decoding invalid input
	ErrInvalidID = errors.New("kid: invalid id")
)

Functions

func Sort

func Sort(ids []ID)

Sort sorts an array of IDs in place.

Types

type ID

type ID [rawLen]byte

ID represents a unique identifier

func FromBytes

func FromBytes(b []byte) (ID, error)

FromBytes copies []bytes into an ID value. Only a length-check is performed.

func FromString

func FromString(str string) (ID, error)

FromString decodes a base32-encoded string to return an ID.

Example
id, err := FromString("03f6nlxczw0018fz")
if err != nil {
	panic(err)
}
fmt.Println(id.Timestamp(), id.Random())
Output:

946684799999 41439

func New

func New() (id ID)

New generates a new unique ID.

This function is goroutine-safe. IDs are composed of:

  • 6 bytes, timestamp, a Unix time in milliseconds
  • 2 bytes, sequence, a derived value ensuring uniqueness and order
  • 2 bytes, random value provided by crypto/rand

K-orderable: Each subsequent call to New() is guaranteed to produce an ID having a timestamp + sequence value greater than the previously generated ID.

Example

examples

id := New()
fmt.Printf(`ID:
    String()  %s
    Timestamp() %d
    Sequence() %d
    Random()  %d 
    Time()    %v
    Bytes()   %3v\n`, id.String(), id.Timestamp(), id.Sequence(), id.Random(), id.Time().UTC(), id.Bytes())
Output:

func (ID) Bytes

func (id ID) Bytes() []byte

Bytes returns the binary representation of id, which is simply id[:].

func (ID) Compare

func (id ID) Compare(other ID) int

Compare makes IDs k-sortable, behaving like `bytes.Compare`, returning 0 if two IDs are identical, -1 if the current ID is less than the other, and 1 if current ID is greater than other.

Note: only the first 8 bytes of the two IDs (timestamp+sequence) are compared.

func (ID) Encode

func (id ID) Encode(dst []byte) []byte

Encode the id using base32 encoding, writing 16 bytes to dst and return it.

func (ID) IsNil

func (id ID) IsNil() bool

IsNil returns true if ID == nilID.

func (ID) IsZero

func (id ID) IsZero() bool

IsZero is an alias of is IsNil.

func (ID) MarshalJSON

func (id ID) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

A json value will always be returned; as a nilID or any other binary ID will always encode, error will always be nil.

https://golang.org/pkg/encoding/json/#Marshaler

func (ID) MarshalText

func (id ID) MarshalText() ([]byte, error)

MarshalText implements `encoding.TextMarshaler`.

As any ID value will always encode, error is always nil. https://golang.org/pkg/encoding/#TextMarshaler

func (ID) Random

func (id ID) Random() int32

Random returns the two-byte random component of the ID.

func (*ID) Scan

func (id *ID) Scan(value any) error

Scan implements the sql.Scanner interface. https://pkg.go.dev/database/sql#Scanner

func (ID) Sequence

func (id ID) Sequence() int32

Sequence returns the ID sequence.

func (ID) String

func (id ID) String() string

String implements `fmt.Stringer`, returning id as a base32 encoded string using the kid custom character set. https://pkg.go.dev/fmt#Stringer

func (ID) Time

func (id ID) Time() time.Time

Time returns the ID's timestamp as a Time value with millisecond resolution and location set to UTC

func (ID) Timestamp

func (id ID) Timestamp() int64

Timestamp returns the timestamp component of id as milliseconds since the Unix epoch. Go timestamps are at location UTC.

func (*ID) UnmarshalJSON

func (id *ID) UnmarshalJSON(b []byte) error

UnmarshalJSON implements the json.Unmarshaler interface. https://golang.org/pkg/encoding/json/#Unmarshaler

func (*ID) UnmarshalText

func (id *ID) UnmarshalText(text []byte) error

UnmarshalText implements `encoding.TextUnmarshaler`, and performs a sanity check on text.

Note: decode() is only called from here and should never fail. https://pkg.go.dev/encoding#TextUnmarshaler

func (ID) Value

func (id ID) Value() (driver.Value, error)

Value implements package sql's driver.Valuer. https://pkg.go.dev/database/sql/driver#Valuer

Directories

Path Synopsis
cmd
kid
A utility to generate or inspect kid.IDs.
A utility to generate or inspect kid.IDs.
eval
compare
Package main produces for comparison purposes a markdown formatted table illustrating key differences between a number of unique ID packages.
Package main produces for comparison purposes a markdown formatted table illustrating key differences between a number of unique ID packages.
uniqcheck
Package main provides a test to determine if ID generation delivers unique IDs for Go applications utilizing concurrency.
Package main provides a test to determine if ID generation delivers unique IDs for Go applications utilizing concurrency.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL