scru64

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2023 License: Apache-2.0 Imports: 7 Imported by: 0

README

SCRU64: Sortable, Clock-based, Realm-specifically Unique identifier

GitHub tag License

SCRU64 ID offers compact, time-ordered unique identifiers generated by distributed nodes. SCRU64 has the following features:

  • 63-bit non-negative integer storable as signed/unsigned 64-bit integer
  • Sortable by generation time (as integer and as text)
  • 12-digit case-insensitive textual representation (Base36)
  • ~38-bit Unix epoch-based timestamp that ensures useful life until year 4261
  • Variable-length node/machine ID and counter fields that share 24 bits
import "os"
import "fmt"
import "github.com/scru64/go-scru64"

// pass node ID through environment variable
os.Setenv("SCRU64_NODE_SPEC", "42/8")

// generate a new identifier object
x := scru64.New()
fmt.Println(x)       // e.g., "0u2r85hm2pt3"
fmt.Println(x.Num()) // as a 64-bit unsigned integer

// generate a textual representation directly
fmt.Println(scru64.NewString()) // e.g., "0u2r85hm2pt4"

See SCRU64 Specification for details.

SCRU64's uniqueness is realm-specific, i.e., dependent on the centralized assignment of node ID to each generator. If you need decentralized, globally unique time-ordered identifiers, consider SCRU128.

License

Licensed under the Apache License, Version 2.0.

See also

Documentation

Overview

SCRU64: Sortable, Clock-based, Realm-specifically Unique identifier

SCRU64 ID offers compact, time-ordered unique identifiers generated by distributed nodes. SCRU64 has the following features:

  • 63-bit non-negative integer storable as signed/unsigned 64-bit integer
  • Sortable by generation time (as integer and as text)
  • 12-digit case-insensitive textual representation (Base36)
  • ~38-bit Unix epoch-based timestamp that ensures useful life until year 4261
  • Variable-length node/machine ID and counter fields that share 24 bits

See SCRU64 Specification for details: https://github.com/scru64/spec

Index

Constants

This section is empty.

Variables

View Source
var ErrClockRollback = fmt.Errorf(
	"scru64.Generator: detected unbearable clock rollback")

The error value returned by Generator.Generate and Generator.GenerateOrAbortCore when the relevant timestamp is significantly smaller than the one embedded in the immediately preceding ID generated by the generator.

View Source
var GlobalGenerator interface {
	// Initializes the global generator, if not initialized, with the node spec
	// passed.
	//
	// This method configures the global generator with the argument only when the
	// global generator is not yet initialized. Otherwise, it preserves the
	// existing configuration.
	//
	// This method return `true` if this method configures the global generator or
	// `false` if it preserves the existing configuration.
	Initialize(nodeSpec NodeSpec) bool

	// Calls `Generator.Generate` of the global generator.
	Generate() (Id, error)

	// Calls `Generator.GenerateOrSleep` of the global generator.
	GenerateOrSleep() Id

	// Calls `Generator.NodeId` of the global generator.
	NodeId() uint32

	// Calls `Generator.NodeIdSize` of the global generator.
	NodeIdSize() uint8

	// Calls `Generator.NodeSpec` of the global generator.
	NodeSpec() NodeSpec
} = &globalGeneratorInner{}

The gateway object that forwards supported method calls to the process-wide global generator.

By default, the global generator reads the node configuration from the `SCRU64_NODE_SPEC` environment variable when a generator method is first called, and it panics if it fails to do so. The node configuration is encoded in a node spec string consisting of `nodeId` and `nodeIdSize` integers separated by a slash (e.g., "42/8", "0xb00/12"; see NodeSpec for details). You can configure the global generator differently by calling `GlobalGenerator.initialize` before the default initializer is triggered.

Functions

func NewString

func NewString() string

Generates a new SCRU64 ID encoded in the 12-digit canonical string representation using the global generator.

By default, the global generator reads the node configuration from the `SCRU64_NODE_SPEC` environment variable when a generator method is first called, and it panics if it fails to do so. The node configuration is encoded in a node spec string consisting of `nodeId` and `nodeIdSize` integers separated by a slash (e.g., "42/8", "0xb00/12"; see NodeSpec for details). You can configure the global generator differently by calling `GlobalGenerator.Initialize()` before the default initializer is triggered.

This function usually returns a value immediately, but if not possible, it sleeps and waits for the next timestamp tick.

This function is thread-safe; multiple threads can call it concurrently.

This function panics if the global generator is not properly configured.

Types

type CounterMode

type CounterMode interface {
	// Returns the next initial counter value of `counterSize` bits.
	//
	// `Generator` passes the `counterSize` (from 1 to 23) and other context
	// information that may be useful for counter renewal. The returned value must
	// be within the range of `counterSize`-bit unsigned integer.
	Renew(counterSize uint8, context CounterModeRenewContext) uint32
}

An interface to customize the initial counter value for each new `timestamp`.

Generator calls `Renew()` to obtain the initial counter value when the `timestamp` field has changed since the immediately preceding ID. Types implementing this interface may apply their respective logic to calculate the initial counter value.

func NewDefaultCounterMode

func NewDefaultCounterMode(overflowGuardSize uint8) CounterMode

Creates a new instance of the default "initialize a portion counter" mode with the size (in bits) of overflow guard bits.

With this mode, the counter is reset to a random number for each new `timestamp` tick, but some specified leading bits are set to zero to reserve space as the counter overflow guard.

Note that the random number generator employed is not cryptographically strong. This mode does not pay for security because a small random number is insecure anyway.

type CounterModeRenewContext

type CounterModeRenewContext struct {
	// The `timestamp` value for the new counter.
	Timestamp uint64

	// The `nodeId` of the generator.
	NodeId uint32
}

Represents the context information provided by Generator to `CounterMode.Renew`.

type Generator

type Generator struct {
	// contains filtered or unexported fields
}

Represents a SCRU64 ID generator.

This structure must be instantiated by one of the dedicated constructors: NewGenerator, NewGeneratorParsing, or NewGeneratorWithCounterMode.

The generator comes with several different methods that generate a SCRU64 ID:

| Flavor              | Timestamp | Thread- | On big clock rewind |
| ------------------- | --------- | ------- | ------------------- |
| Generate            | Now       | Safe    | Returns error       |
| GenerateOrReset     | Now       | Safe    | Resets generator    |
| GenerateOrSleep     | Now       | Safe    | Sleeps              |
| GenerateOrAbortCore | Argument  | Unsafe  | Returns error       |
| GenerateOrResetCore | Argument  | Unsafe  | Resets generator    |

All of these methods return a monotonically increasing ID by reusing the previous `timestamp` even if the one provided is smaller than the immediately preceding ID's, unless such a clock rollback is considered significant (by default, approx. 10 seconds). A clock rollback may also be detected when a generator has generated too many IDs within a certain unit of time, because this implementation increments the previous `timestamp` when `counter` reaches the limit to continue instant monotonic generation. When a significant clock rollback is detected:

  1. `Generate` (OrAbort) methods abort and return the ErrClockRollback error value immediately.
  2. `OrReset` variants reset the generator and return a new ID based on the given `timestamp`, breaking the increasing order of IDs.
  3. `OrSleep` method sleeps and waits for the next timestamp tick.

The `Core` functions offer low-level thread-unsafe primitives to customize the behavior.

func NewGenerator

func NewGenerator(nodeSpec NodeSpec) *Generator

Creates a new generator with the given node configuration.

func NewGeneratorParsing

func NewGeneratorParsing(nodeSpec string) *Generator

Creates a new generator by parsing the given node spec string.

This is a shortcut constructor that chains ParseNodeSpec and NewGenerator for convenience, panicking if the argument is not a valid node spec string.

func NewGeneratorWithCounterMode

func NewGeneratorWithCounterMode(
	nodeSpec NodeSpec, counterMode CounterMode) *Generator

Creates a new generator with the given node configuration and counter initialization mode.

This constructor panics if `counterMode` is nil.

func (*Generator) Generate

func (g *Generator) Generate() (Id, error)

Generates a new SCRU64 ID object from the current `timestamp`, or returns an error upon significant timestamp rollback.

See the Generator type documentation for the description.

This method returns the ErrClockRollback error upon significant clock rollback.

func (*Generator) GenerateOrAbortCore

func (g *Generator) GenerateOrAbortCore(
	unixTsMs uint64, rollbackAllowance uint64) (Id, error)

Generates a new SCRU64 ID object from a Unix timestamp in milliseconds, or returns an error upon significant timestamp rollback.

See the Generator type documentation for the description.

The `rollbackAllowance` parameter specifies the amount of `unixTsMs` rollback that is considered significant. A suggested value is `10_000` (milliseconds).

Unlike Generator.Generate, this method is NOT thread-safe. The generator object should be protected from concurrent accesses using a mutex or other synchronization mechanism to avoid race conditions.

This method returns the ErrClockRollback error upon significant clock rollback.

This method panics if `unixTsMs` is not a positive integer within the valid range.

func (*Generator) GenerateOrReset

func (g *Generator) GenerateOrReset() Id

Generates a new SCRU64 ID object from the current `timestamp`, or resets the generator upon significant timestamp rollback.

See the Generator type documentation for the description.

Note that this mode of generation is not recommended because rewinding `timestamp` without changing `nodeId` considerably increases the risk of duplicate results.

func (*Generator) GenerateOrResetCore

func (g *Generator) GenerateOrResetCore(
	unixTsMs uint64, rollbackAllowance uint64) Id

Generates a new SCRU64 ID object from a Unix timestamp in milliseconds, or resets the generator upon significant timestamp rollback.

See the Generator type documentation for the description.

Note that this mode of generation is not recommended because rewinding `timestamp` without changing `nodeId` considerably increases the risk of duplicate results.

The `rollbackAllowance` parameter specifies the amount of `unixTsMs` rollback that is considered significant. A suggested value is `10_000` (milliseconds).

Unlike Generator.GenerateOrReset, this method is NOT thread-safe. The generator object should be protected from concurrent accesses using a mutex or other synchronization mechanism to avoid race conditions.

This method panics if `unixTsMs` is not a positive integer within the valid range.

func (*Generator) GenerateOrSleep

func (g *Generator) GenerateOrSleep() Id

Returns a new SCRU64 ID object, or sleeps and waits for one if not immediately available.

See the Generator type documentation for the description.

func (*Generator) NodeId

func (g *Generator) NodeId() uint32

Returns the `nodeId` of the generator.

func (*Generator) NodeIdSize

func (g *Generator) NodeIdSize() uint8

Returns the size in bits of the `nodeId` adopted by the generator.

func (*Generator) NodeSpec

func (g *Generator) NodeSpec() NodeSpec

Returns the node configuration specifier describing the generator state.

type Id

type Id uint64

Represents a SCRU64 ID.

const MaxId Id = 4738381338321616895

The maximum valid SCRU64 ID value (i.e., `zzzzzzzzzzzz`).

const MinId Id = 0

The minimum valid SCRU64 ID value (i.e., `000000000000`).

func FromParts

func FromParts(timestamp uint64, nodeCtr uint32) (Id, error)

Creates a value from the `timestamp` and the combined `nodeCtr` field value.

This function returns a non-nil error if any argument is larger than their respective maximum value (`36^12 / 2^24 - 1` and `2^24 - 1`, respectively).

func FromUint

func FromUint(value uint64) (Id, error)

Creates a value from a 64-bit unsigned integer.

This function returns a non-nil error if the argument is larger than `36^12 - 1`.

func New

func New() Id

Generates a new SCRU64 ID object using the global generator.

By default, the global generator reads the node configuration from the `SCRU64_NODE_SPEC` environment variable when a generator method is first called, and it panics if it fails to do so. The node configuration is encoded in a node spec string consisting of `nodeId` and `nodeIdSize` integers separated by a slash (e.g., "42/8", "0xb00/12"; see NodeSpec for details). You can configure the global generator differently by calling `GlobalGenerator.Initialize()` before the default initializer is triggered.

This function usually returns a value immediately, but if not possible, it sleeps and waits for the next timestamp tick.

This function is thread-safe; multiple threads can call it concurrently.

This function panics if the global generator is not properly configured.

func Parse

func Parse(value string) (Id, error)

Creates a value from a 12-digit string representation.

This function returns a non-nil error if the argument is not a valid string representation.

func (Id) MarshalText

func (n Id) MarshalText() (text []byte, err error)

See encoding.TextMarshaler

func (Id) NodeCtr

func (n Id) NodeCtr() uint32

Returns the `nodeId` and `counter` field values combined as a single 24-bit integer.

func (Id) Num added in v0.2.0

func (n Id) Num() uint64

Returns the integer representation.

func (*Id) Scan

func (n *Id) Scan(src any) error

See database/sql.Scanner

func (Id) String

func (n Id) String() string

Returns the 12-digit canonical string representation.

func (Id) Timestamp

func (n Id) Timestamp() uint64

Returns the `timestamp` field value.

func (*Id) UnmarshalText

func (n *Id) UnmarshalText(text []byte) error

See encoding.TextUnmarshaler

type NodeSpec

type NodeSpec struct {
	// contains filtered or unexported fields
}

Represents a node configuration specifier used to build a Generator.

A `NodeSpec` is usually expressed as a node spec string, which starts with a decimal `nodeId`, a hexadecimal `nodeId` prefixed by "0x", or a 12-digit `nodePrev` SCRU64 ID value, followed by a slash and a decimal `nodeIdSize` value ranging from 1 to 23 (e.g., "42/8", "0xb00/12", "0u2r85hm2pt3/16"). The first and second forms create a fresh new generator with the given `nodeId`, while the third form constructs one that generates subsequent SCRU64 IDs to the `nodePrev`. See also the usage notes in the SCRU64 spec for tips and techniques to design node configurations.

func NewNodeSpecWithNodeId

func NewNodeSpecWithNodeId(nodeId uint32, nodeIdSize uint8) (NodeSpec, error)

Creates an instance of NodeSpec with `nodeId` and `nodeIdSize` values.

This function returns a non-nil error if the `nodeIdSize` is zero or greater than 23 or if the `nodeId` does not fit in `nodeIdSize` bits.

func NewNodeSpecWithNodePrev

func NewNodeSpecWithNodePrev(nodePrev Id, nodeIdSize uint8) (NodeSpec, error)

Creates an instance of NodeSpec with `nodePrev` and `nodeIdSize` values.

This function returns a non-nil error if the `nodeIdSize` is zero or greater than 23.

func ParseNodeSpec

func ParseNodeSpec(value string) (NodeSpec, error)

Creates an instance of NodeSpec from a node spec string.

This function returns a non-nil error if if an invalid node spec string is passed.

func (NodeSpec) MarshalText

func (n NodeSpec) MarshalText() (text []byte, err error)

See encoding.TextMarshaler

func (NodeSpec) NodeId

func (n NodeSpec) NodeId() uint32

Returns the `nodeId` value given at instance creation or encoded in the `nodePrev` value.

func (NodeSpec) NodeIdSize

func (n NodeSpec) NodeIdSize() uint8

Returns the `nodeIdSize` value.

func (NodeSpec) NodePrev

func (n NodeSpec) NodePrev() Id

Returns the `nodePrev` value if the `NodeSpec` is constructed with one or the zero value (`scru64.Id(0)`) otherwise.

func (*NodeSpec) Scan

func (n *NodeSpec) Scan(src any) error

See database/sql.Scanner

func (NodeSpec) String

func (n NodeSpec) String() string

Returns the node spec string representation.

func (*NodeSpec) UnmarshalText

func (n *NodeSpec) UnmarshalText(text []byte) error

See encoding.TextUnmarshaler

Jump to

Keyboard shortcuts

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