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 (ChaCha8, seeded by the Go runtime from OS entropy).
IDs encode (base32) as 16-byte URL-friendly strings. The encoding alphabet is in ascending ASCII order, so the encoded form preserves the sort order of the binary form: IDs are k-orderable in either representation.
kid.ID features:
- Size: 10 bytes as binary, 16 bytes if stored/transported as an encoded string.
- Timestamp + sequence is guaranteed to be unique and monotonically increasing within a process, even if the wall clock steps backwards.
- Lock-free generation: New scales with cores instead of serializing on a mutex.
- 2 bytes of trailing randomness from math/rand/v2's ChaCha8 generator.
- 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.
Capacity: uniqueness is carried entirely by the timestamp+sequence pair, giving 4,096 IDs per millisecond (~4.1 million/second) sustained, per process. Bursts beyond that rate borrow sequence slots from future milliseconds: IDs remain unique and strictly k-sortable, but their embedded timestamps lead the wall clock until generation slows. Treat the embedded time as approximate metadata rather than an exact wall-clock instant.
Security note: an ID carries only 16 bits of randomness alongside values derived from the clock; IDs are predictable by design. Do not use kid IDs where unguessability matters, such as session tokens, API keys, or password reset codes.
Example usage:
func main() {
id := kid.New()
fmt.Printf("%s %03v\n", id, id[:])
// Example output: 06bq7xhnr03mlz6r [001 149 115 246 021 192 007 073 252 216]
id, err := kid.FromString("06bq7xhnr03mlz6r")
if err != nil {
// handle the error
}
fmt.Printf("%s %03v\n", id, id[:])
// Output: 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. The timestamp+sequence encoding is derived from the google/uuid getV7Time() algorithm, with its mutex protection replaced by a lock-free atomic claim. Third-party copyright notices and license texts are reproduced in the NOTICES file.
Index ¶
- Variables
- func Sort(ids []ID)
- type ID
- func (id ID) Bytes() []byte
- func (id ID) Compare(other ID) int
- func (id ID) Encode(dst []byte) []byte
- func (id ID) IsNil() bool
- func (id ID) IsZero() bool
- func (id ID) MarshalJSON() ([]byte, error)
- func (id ID) MarshalText() ([]byte, error)
- func (id ID) Random() int32
- func (id *ID) Scan(value any) error
- func (id ID) Sequence() int32
- func (id ID) String() string
- func (id ID) Time() time.Time
- func (id ID) Timestamp() int64
- func (id *ID) UnmarshalJSON(b []byte) error
- func (id *ID) UnmarshalText(text []byte) error
- func (id ID) Value() (driver.Value, error)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidID represents an error state, typically when decoding invalid input ErrInvalidID = errors.New("kid: invalid id") )
Functions ¶
Types ¶
type ID ¶
type ID [rawLen]byte
ID represents a unique identifier
func FromString ¶
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 from math/rand/v2's ChaCha8 generator
New is lock-free and free of retry loops: the timestamp+sequence is claimed with at most one atomic compare-and-swap followed, if needed, by a wait-free atomic increment, and the random bytes are drawn from math/rand/v2's per-goroutine ChaCha8 state (seeded by the Go runtime from OS entropy), so ID generation scales with cores rather than serializing on a mutex or spinning under contention.
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
`, id.String(), id.Timestamp(), id.Sequence(), id.Random(), id.Time().UTC(), id.Bytes())
func (ID) Compare ¶
Compare returns an integer comparing two IDs with `bytes.Compare` semantics: 0 if the IDs are identical, -1 if id is less than other, and 1 if id is greater than other. All 10 bytes participate, so Compare is consistent with ==; because the timestamp and sequence occupy the leading bytes, IDs order by creation time first.
func (ID) Encode ¶
Encode the id using base32 encoding, writing 16 bytes to dst and returning it. Encode panics if len(dst) < 16.
func (ID) MarshalJSON ¶
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.
func (ID) MarshalText ¶
MarshalText implements `encoding.TextMarshaler`.
As any ID value will always encode, error is always nil. https://golang.org/pkg/encoding/#TextMarshaler
func (*ID) Scan ¶
Scan implements the sql.Scanner interface, accepting the 16-byte encoded form as a string or []byte, the 10-byte binary form as a []byte, or nil, which yields the nil ID. https://pkg.go.dev/database/sql#Scanner
func (ID) Sequence ¶
Sequence returns the sequence component of id.
For IDs produced by New, the sequence is a 12-bit value (0-4095); if a burst of calls would overflow the sequence within a single millisecond, the overflow carries into the timestamp, preserving order (see getTS). The field occupies two bytes, so IDs from other sources may carry larger values.
func (ID) 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 ¶
Time returns the ID's timestamp as a Time value with millisecond resolution and location set to UTC
func (ID) Timestamp ¶
Timestamp returns the timestamp component of id as milliseconds since the Unix epoch. Go timestamps are at location UTC.
func (*ID) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface, accepting only null or a quoted 16-character kid encoding. https://golang.org/pkg/encoding/json/#Unmarshaler
func (*ID) UnmarshalText ¶
UnmarshalText implements `encoding.TextUnmarshaler`. text must be a 16-byte base32-encoded value over the kid alphabet; on error, id is set to the nil ID and ErrInvalidID is returned. https://pkg.go.dev/encoding#TextUnmarshaler
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
kid
command
A utility to generate or inspect kid.IDs.
|
A utility to generate or inspect kid.IDs. |
|
eval
|
|
|
compare
command
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
command
Command uniqcheck verifies, under concurrent load, the two guarantees made by kid.New:
|
Command uniqcheck verifies, under concurrent load, the two guarantees made by kid.New: |