Documentation
ΒΆ
Overview ΒΆ
Package guid implements interface to generate k-ordered unique identifiers in lock-free and decentralized manner for Golang applications. We says that sequence A is k-ordered if it consists of strictly ordered subsequences of length k:
π¨[π β π] β€ π¨[π] β€ π¨[π + π] for all π such that π < π β€ πβπ.
Key features ΒΆ
This library aims important objectives:
β£ IDs allocation does not require centralized authority or coordination with other nodes.
β£ IDs are suitable for partial event ordering in distributed environment and helps on detection of causality violation.
β£ IDs are roughly sortable by allocation order ("time").
β£ IDs reduce indexes footprints and optimize lookup latency.
Inspiration ΒΆ
The event ordering in distributed computing is resolved using various techniques, e.g. Lamport timestamps (https://en.wikipedia.org/wiki/Lamport_timestamps), Universal Unique Identifiers (https://tools.ietf.org/html/rfc4122), Twitter Snowflake (https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake.html) and many other techniques are offered by open source libraries. `guid` is a Golang port of https://github.com/fogfish/uid.
All these solution made a common conclusion, globally unique ID is a triple β¨π, π, πβ©:
β£ β¨πβ© monotonically increasing clock or timestamp is a primary dimension to roughly sort events,
β£ β¨πβ© is spatially unique identifier of ID allocator so called node location,
β£ β¨πβ© sequence is a monotonic integer, which prevents clock collisions. The value is global for the node
The `guid` library addresses few issues observed in other solutions.
Every byte counts when application is processing or storing large volume of events. This library implements fixed size 96-bit identity schema, which is castable to 64-bit under certain occasion. It is about 25% improvement to compare with UUID or similar 128-bit identity schemas. Twitters Snowflake is also 64-bit.
Most of identity schemas uses monotonically increasing clock (timestamp) to roughly order events. The resolution of clock varies from nanoseconds to milliseconds. We found that usage of timestamp is not perfectly aligned with the goal of decentralized ID allocations. Usage of time synchronization protocol becomes necessary at distributed systems. Strictly speaking, NTP server becomes an authority to coordinate clock synchronization. This happens because schemas uses time fraction β¨πβ© as a primary sorting key. In contrast with other libraries, `guid` do not give priority to single β¨πβ© or β¨πβ© fraction of identity triple. It uses dynamic schema where the location fraction has higher priority than time only at particular precision. It allows to keep ordering consistent even if clocks on other node is skewed.
Identity Schema ΒΆ
A fixed size of 96-bit is used to implement identity schema
3bit 47 bit - π bit 32 bit π bit 14 bit |-|-------------------|----------------|-----|-------| β¨π β© β¨πβ© β¨πβ© β¨πβ© β¨πβ©
β£ β¨πβ© is 47-bit UTC timestamp with millisecond precision. It is derived from nanosecond UNIX timesamp by shifting it by 17 bits (time.Now().UnixNano() >> 17). The library is able to change the base timestamp to any value in-order to address Year 2038 problem.
β£ β¨πβ© is 32-bits node/spacial identifier. It is allocated randomly to each node using cryptographic random generator or application provided value. The node identity has higher sorting priority than seconds faction of timestamp. This allows to order events if clock drifts on nodes. The random allocation give an application ability to introduce about 65K allocators before it meets a high probability of collisions.
β£ β¨π β© is 3 drift bits defines allowed clock drift. It shows the value of less important faction of time. The value supports step-wise drift from 30 seconds to 36 minutes.
β£ β¨πβ© is 14-bit of monotonic strictly locally ordered integer. It helps to avoid collisions when multiple events happens during single millisecond or when the clock set backwards. The 14-bit value allows to have about 16K allocations per millisecond and over 10M per second on single node. Each instance of application process runs a unique sequence of integers. The implementation ensures that the same integer is not returned more than once on the current process. Restart of the process resets the sequence.
The library supports casting of 96-bit identifier to 64-bit by dropping β¨πβ© fraction. This optimization reduces a storage footprint if application uses persistent allocators.
3bit 47 bit 14 bit |-|------------------------|-------| β¨π β© β¨πβ© β¨πβ©
Applications ΒΆ
The schema has wide range of applications where globally unique id are required.
β£ object identity: use library to generate unique identifiers.
β£ replacement of auto increment types: out-of-the-box replacement for auto increment fields in databases.
β£ vector clock: defines a logical clock for each process.
β£ CRDTs: LWW Register, OR-set and others.
Index ΒΆ
- func After(a, b K) bool
- func Base62(uid K) string
- func Before(a, b K) bool
- func Bytes(uid K) []byte
- func EpochI(uid K) time.Time
- func EpochT(uid K) time.Time
- func Equal(a, b K) bool
- func Node(uid K) uint64
- func Seq(uid K) uint64
- func Split(n uint64, uid K) (bytes []byte)
- func String(uid K) string
- func Time(uid K) uint64
- type Chronos
- type Config
- type K
- func Diff(a, b K) K
- func FoldG(n uint64, bytes []byte) (uid K)
- func FoldL(n uint64, bytes []byte) (uid K)
- func FromBase62(val string) (K, error)
- func FromBytes(val []byte) (K, error)
- func FromL(clock Chronos, uid K) K
- func FromStringG(val string) (K, error)
- func FromStringL(val string) (K, error)
- func FromT(t time.Time, drift ...time.Duration) K
- func G(clock Chronos, drift ...time.Duration) K
- func L(clock Chronos, drift ...time.Duration) K
- func ToL(uid K) K
- func Z(clock Chronos, drift ...time.Duration) (uid K)
Constants ΒΆ
This section is empty.
Variables ΒΆ
This section is empty.
Functions ΒΆ
func EpochI ΒΆ
EpochI (inverse) convers β¨πβ© timestamp fraction from identifier as unix timestamp
func Seq ΒΆ
Seq returns β¨πβ© sequence value. The value of monotonic unique integer at the time of K-ordered value creation.
func Split ΒΆ
Split decomposes UID value to bytes slice. The function acts as binary comprehension, the value n defines number of bits to extract into each cell.
Types ΒΆ
type Chronos ΒΆ
type Chronos interface {
// Spatially unique identifier β¨πβ© of ID allocator so called node location
L() uint64
// Monotonically increasing logical clock β¨πβ©
T() (uint64, uint64)
}
Chronos is an abstraction of logical clock used by library.
Clock is global default instance of logical clock
If the application needs own default clock e.g. inverse one, it declares own clock and pair of GID & LID functions.
func NewClockMock ΒΆ
Create mock instance of logical clock
type Config ΒΆ
type Config func(*clock)
Config option of default logical clock behavior. Config options allows to define custom strategies to generate β¨πβ© location or β¨πβ© timestamp.
func WithClockInverse ΒΆ
func WithClockInverse() Config
WithClockInverse configures inverse unix timestamp as generator function
func WithClockUnix ΒΆ
func WithClockUnix() Config
WithClockUnix configures unix timestamp time.Now().UnixNano() as generator function
func WithNodeFromEnv ΒΆ
func WithNodeFromEnv() Config
WithNodeFromEnv configures β¨πβ© spatially unique identifier using env variable.
CONFIG_GUID_NODE_ID - defines location id as a string
func WithNodeID ΒΆ
WithNodeID explicitly configures β¨πβ© spatially unique identifier
func WithNodeRandom ΒΆ
func WithNodeRandom() Config
WithNodeRandom configures β¨πβ© spatially unique identifier using cryptographic random generator
func WithUnique ΒΆ
WithUnique configures generator for β¨πβ© monotonic strictly locally ordered integer
type K ΒΆ added in v2.0.2
type K struct{ Hi, Lo uint64 }
K is native representation of k-ordered number. The structure is dedicated for both local and global k-ordered values. The local k-ordered value do not uses Hi fraction (equal to 0). The global k-ordered value is 96-bit long and requires no central registration process.
Note: Golang struct is 128-bits but only 96-bits are used effectively. The serialization process ensures that only 96-bits are used.
func FromBase62 ΒΆ added in v2.1.0
FromBase62 decodes converts k-order UID from base62 string
func FromStringG ΒΆ added in v2.0.2
FromStringG decodes converts k-order UID from lexicographically sortable strings
func FromStringL ΒΆ added in v2.0.2
FromStringL decodes converts k-order UID from lexicographically sortable strings
func G ΒΆ
Generates globally unique 96-bit k-ordered identifier.
3bit 47 bit - π bit 32 bit π bit 14 bit |-|-------------------|----------------|-----|-------| β¨π β© β¨πβ© β¨πβ© β¨πβ© β¨πβ©
func (K) MarshalJSON ΒΆ added in v2.0.2
MarshalJSON encodes k-ordered value to lexicographically sortable JSON strings
func (*K) UnmarshalJSON ΒΆ added in v2.0.2
UnmarshalJSON decodes lexicographically sortable strings to UID value