snowflake

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package snowflake is a standard-library-only implementation of the Twitter Snowflake distributed ID scheme, echoing the design of popular npm libraries such as "snowflake-id" and the Go package bwmarrin/snowflake. It generates 63-bit integer identifiers that are unique across many nodes, roughly ordered by creation time, and cheap to produce at high throughput without any shared state between machines beyond a distinct node id per generator.

Each id is a 63-bit value (it fits in a signed int64 and stays non-negative) partitioned into three fields. The high 41 bits hold a millisecond timestamp measured relative to a custom epoch; the middle 10 bits hold the node id (0..1023); and the low 12 bits hold a per-millisecond sequence (0..4095). The custom Epoch constant is the Twitter epoch, 1288834974657 ms (2010-11-04 01:42:54.657 UTC); subtracting it from the wall clock lets the 41-bit timestamp field span about 69 years from that starting point rather than from 1970.

A Node bundles a node id with the small amount of mutable state needed to assign sequence numbers, guarded by a mutex so a single Node is safe for concurrent use. NewNode validates the node id against the 10-bit range and returns an error if it is out of bounds. GenerateAt composes an id for an explicit millisecond timestamp, and Generate is the convenience wrapper that calls GenerateAt with time.Now().UnixMilli().

Monotonicity is handled by the sequence field. When GenerateAt is called repeatedly within the same millisecond, the sequence increments so each id is distinct and strictly increasing; if the 12-bit sequence overflows (more than 4096 ids in one millisecond), the generator advances its internal timestamp by one millisecond and continues, effectively borrowing from the future to preserve ordering. When the clock moves forward to a new millisecond the sequence resets to zero. Because the timestamp occupies the most significant bits, ids from a single node increase over time and are broadly sortable across nodes, though ids minted in the same millisecond on different nodes are ordered by node id rather than by true creation instant.

The package-level helpers Timestamp, NodeOf, and Sequence decompose an id back into its absolute Unix-millisecond time, node id, and sequence. Compared with the reference JavaScript and Go implementations, the 41/10/12 bit layout, the Twitter epoch, and the overflow-then-advance behaviour match; the main differences are the idiomatic Go Node type with explicit GenerateAt for deterministic testing and the plain int64 return type instead of a string or BigInt wrapper.

Index

Examples

Constants

View Source
const (
	// Epoch is the custom epoch in Unix milliseconds (the Twitter epoch,
	// 2010-11-04 01:42:54.657 UTC).
	Epoch int64 = 1288834974657
)

Variables

This section is empty.

Functions

func NodeOf

func NodeOf(id int64) int64

NodeOf returns the node id encoded in id.

func Sequence

func Sequence(id int64) int64

Sequence returns the per-millisecond sequence encoded in id.

func Timestamp

func Timestamp(id int64) int64

Timestamp returns the absolute Unix-millisecond timestamp encoded in id.

Types

type Node

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

Node generates snowflake ids for a single node id.

func NewNode

func NewNode(nodeID int64) (*Node, error)

NewNode returns a Node for the given node id (0..1023).

func (*Node) Generate

func (n *Node) Generate() int64

Generate produces a new id using the current wall-clock time.

func (*Node) GenerateAt

func (n *Node) GenerateAt(ms int64) int64

GenerateAt produces an id for the given millisecond timestamp. Repeated calls with the same timestamp increment the sequence; on sequence overflow the timestamp is advanced by one millisecond.

Example

ExampleNode_GenerateAt builds an id for an explicit millisecond timestamp so the result is fully deterministic and can be decomposed. GenerateAt is used instead of Generate (which reads the wall clock) precisely so the example does not depend on the current time. The package-level helpers then extract the three fields packed into the 63-bit value: the node id, the per-millisecond sequence, and the absolute timestamp. Here the node id is 1, the sequence is 0 because it is the first id in that millisecond, and the decoded timestamp equals the input. This shows the timestamp round-trips exactly.

package main

import (
	"fmt"

	"github.com/malcolmston/express/snowflake"
)

func main() {
	node, _ := snowflake.NewNode(1)
	id := node.GenerateAt(snowflake.Epoch + 1000)
	fmt.Println(snowflake.NodeOf(id))
	fmt.Println(snowflake.Sequence(id))
	fmt.Println(snowflake.Timestamp(id) == snowflake.Epoch+1000)
}
Output:
1
0
true
Example (Sequence)

ExampleNode_GenerateAt_sequence shows how monotonicity is preserved within a single millisecond. Two calls with the same timestamp cannot share an id, so the generator increments the 12-bit sequence field: the first id gets sequence 0 and the second gets sequence 1. This guarantees the two ids are distinct and strictly increasing even though their timestamps are identical. If the sequence were to overflow past 4095 the generator would advance to the next millisecond. Sequence numbering resets to zero whenever the clock moves to a new millisecond.

package main

import (
	"fmt"

	"github.com/malcolmston/express/snowflake"
)

func main() {
	node, _ := snowflake.NewNode(0)
	ms := snowflake.Epoch + 2000
	a := node.GenerateAt(ms)
	b := node.GenerateAt(ms)
	fmt.Println(snowflake.Sequence(a), snowflake.Sequence(b))
}
Output:
0 1

Jump to

Keyboard shortcuts

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