A simple to use Go (golang) package to generate or parse Twitter snowflake IDs

snowflake

GoDoc Go report Coverage Build Status Discord Gophers

snowflake is a Go package that provides

  • A very simple Twitter snowflake generator.
  • Methods to parse existing snowflake IDs.
  • Methods to convert a snowflake ID into several other data types and back.
  • JSON Marshal/Unmarshal functions to easily use snowflake IDs within a JSON API.
  • Monotonic Clock calculations protect from clock drift.

For help with this package or general Go discussion, please join the Discord Gophers chat server.

Status

This package should be considered stable and completed. Any additions in the future will strongly avoid API changes to existing functions.

ID Format

By default, the ID format follows the original Twitter snowflake format.

  • The ID as a whole is a 63 bit integer stored in an int64
  • 41 bits are used to store a timestamp with millisecond precision, using a custom epoch.
  • 10 bits are used to store a node id - a range from 0 through 1023.
  • 12 bits are used to store a sequence number - a range from 0 through 4095.

Custom Format

You can alter the number of bits used for the node id and step number (sequence) by setting the snowflake.NodeBits and snowflake.StepBits values. Remember that There is a maximum of 22 bits available that can be shared between these two values. You do not have to use all 22 bits.

Custom Epoch

By default this package uses the Twitter Epoch of 1288834974657 or Nov 04 2010 01:42:54. You can set your own epoch value by setting snowflake.Epoch to a time in milliseconds to use as the epoch.

Custom Notes

When setting custom epoch or bit values you need to set them prior to calling any functions on the snowflake package, including NewNode(). Otherwise the custom values you set will not be applied correctly.

How it Works.

Each time you generate an ID, it works, like this.

  • A timestamp with millisecond precision is stored using 41 bits of the ID.
  • Then the NodeID is added in subsequent bits.
  • Then the Sequence Number is added, starting at 0 and incrementing for each ID generated in the same millisecond. If you generate enough IDs in the same millisecond that the sequence would roll over or overfill then the generate function will pause until the next millisecond.

The default Twitter format shown below.

+--------------------------------------------------------------------------+
| 1 Bit Unused | 41 Bit Timestamp |  10 Bit NodeID  |   12 Bit Sequence ID |
+--------------------------------------------------------------------------+

Using the default settings, this allows for 4096 unique IDs to be generated every millisecond, per Node ID.

Getting Started

Installing

This assumes you already have a working Go environment, if not please see this page first.

go get github.com/bwmarrin/snowflake

Usage

Import the package into your project then construct a new snowflake Node using a unique node number. The default settings permit a node number range from 0 to 1023. If you have set a custom NodeBits value, you will need to calculate what your node number range will be. With the node object call the Generate() method to generate and return a unique snowflake ID.

Keep in mind that each node you create must have a unique node number, even across multiple servers. If you do not keep node numbers unique the generator cannot guarantee unique IDs across all nodes.

Example Program:

package main

import (
	"fmt"

	"github.com/bwmarrin/snowflake"
)

func main() {

	// Create a new Node with a Node number of 1
	node, err := snowflake.NewNode(1)
	if err != nil {
		fmt.Println(err)
		return
	}

	// Generate a snowflake ID.
	id := node.Generate()

	// Print out the ID in a few different ways.
	fmt.Printf("Int64  ID: %d\n", id)
	fmt.Printf("String ID: %s\n", id)
	fmt.Printf("Base2  ID: %s\n", id.Base2())
	fmt.Printf("Base64 ID: %s\n", id.Base64())

	// Print out the ID's timestamp
	fmt.Printf("ID Time  : %d\n", id.Time())

	// Print out the ID's node number
	fmt.Printf("ID Node  : %d\n", id.Node())

	// Print out the ID's sequence number
	fmt.Printf("ID Step  : %d\n", id.Step())

  // Generate and print, all in one.
  fmt.Printf("ID       : %d\n", node.Generate().Int64())
}

Performance

With default settings, this snowflake generator should be sufficiently fast enough on most systems to generate 4096 unique ID's per millisecond. This is the maximum that the snowflake ID format supports. That is, around 243-244 nanoseconds per operation.

Since the snowflake generator is single threaded the primary limitation will be the maximum speed of a single processor on your system.

To benchmark the generator on your system run the following command inside the snowflake package directory.

go test -run=^$ -bench=.

If your curious, check out this commit that shows benchmarks that compare a few different ways of implementing a snowflake generator in Go.

Comments
  • Use Monotonic clock

    Use Monotonic clock

    Starting from Go 1.9, the standard time package transparently uses Monotonic Clocks when available. Let's use that for generating ids to safeguard against wall clock backwards movement which could be caused by time drifts or leap seconds.

    This PR addresses the aforementioned issue which may lead to collisions.

  • Check for clock drift

    Check for clock drift

    The current implementation does not check for clock drift when generating snowflakes. This would be fairly easy to implement a basic check, however I think the cleanest way would be to change the Generate() function signature to return an error as well.

    I understand this is a breaking API change you may not want. I am wondering if you are open to this change in any form. This could be breaking the API, deploying versioned packages with something like gopkg.in, creating a new API, etc...

    Let me know what you think.

  • got repeated id

    got repeated id

    hi,

    i've tested with my laptop on window7, call the function test_id in example for several times,

    test_id()
    

    // time.Sleep(1) test_id() // time.Sleep(1) test_id() // time.Sleep(1) test_id() // time.Sleep(1) test_id() // time.Sleep(1)

    and got ======log============== Int64 ID: 890399407000784896 String ID: 890399407000784896 Base2 ID: 110001011011010101011000101100001010100000000001000000000000 Base64 ID: ODkwMzk5NDA3MDAwNzg0ODk2 ID Time : 1501122736107 ID Node : 1 ID Step : 0 ID : 890399407000784897 Int64 ID: 890399407000784896 String ID: 890399407000784896 Base2 ID: 110001011011010101011000101100001010100000000001000000000000 Base64 ID: ODkwMzk5NDA3MDAwNzg0ODk2 ID Time : 1501122736107 ID Node : 1 ID Step : 0 ID : 890399407000784897 Int64 ID: 890399407000784896 String ID: 890399407000784896 Base2 ID: 110001011011010101011000101100001010100000000001000000000000 Base64 ID: ODkwMzk5NDA3MDAwNzg0ODk2 ID Time : 1501122736107 ID Node : 1 ID Step : 0 ID : 890399407000784897 Int64 ID: 890399407000784896 String ID: 890399407000784896 Base2 ID: 110001011011010101011000101100001010100000000001000000000000 Base64 ID: ODkwMzk5NDA3MDAwNzg0ODk2 ID Time : 1501122736107 ID Node : 1 ID Step : 0 ID : 890399407000784897 Int64 ID: 890399407000784896 String ID: 890399407000784896 Base2 ID: 110001011011010101011000101100001010100000000001000000000000 Base64 ID: ODkwMzk5NDA3MDAwNzg0ODk2 ID Time : 1501122736107 ID Node : 1 ID Step : 0 ID : 890399407000784897

    the id are the same~~~~~~~

    calling with time delay, the id changed, test_id() time.Sleep(1) test_id() time.Sleep(1) test_id() time.Sleep(1) test_id() time.Sleep(1)

    seems the id isn't increased

  • 生成的ID会重复

    生成的ID会重复

    func NextIdentity() (int64, error) { //TODO: NodeID 换成读取配置 node, err := snowflake.NewNode(1)

    if err != nil {
    	Logger().Info(
    		"Generate identity fail.",
    		zap.String("error", err.Error()),
    	)
    }
    
    ID := node.Generate()
    return ID.Int64(), nil
    

    }

    r.GET("/testOrder", func(c *gin.Context) { fmt.Println(utils.NextIdentity()) }

    ab -c 10 -n 11 http://localhost:8080/testOrder

    就在10个并发的时候就会重复

  • Remove deprecated parts of code

    Remove deprecated parts of code

    These are backward compatibility breaking changes WRT the config aspect. If the client-code does not specify Epoch, NodeBits or StepBits then there is very little ill effect.

    Wraps all node config into a config object and moves global settings into a private global config object.

  • Add parsers

    Add parsers

    We've got many methods to convert a snowflake into different formats but no methods to convert from those formats back into a snowflake. There should be a 1:1 pair for all formats.

  • Add a method to encode the snowflake as a byte slice

    Add a method to encode the snowflake as a byte slice

    Previously there was a method called Bytes() []byte, but that simply returned the string representation as a byte slice. Not too efficient for storage and transport. This PR adds a method which returns a raw representation of the ID as an 8-bit byte array.

    Also marshalling it into a byte array is ~10x faster than the JSON marshaller, since it looks like you folks like benchmarks 😉

    BenchmarkMarshal-4      10000000           172 ns/op          32 B/op          1 allocs/op
    BenchmarkIntMarshal-4   100000000           17.1 ns/op         0 B/op          0 allocs/op
    
  • Data race-unsafe use of package-level variables

    Data race-unsafe use of package-level variables

    Global package-level variables are being used unsafely from the perspective of the race detector. For example, trying to use NewNode() and Generate() at the same time will trip the race detector for nodeShift in:

    snowflake/snowflake.go:97
    snowflake/snowflake.go:132
    

    Steps to reproduce:

    • Run NewNode() and Generate() in parallel, such as via tests using t.Parallel().
    • Use race detector when running tests.

    Other package-level variables besides nodeShift may also affected.

  • Remove deprecated

    Remove deprecated

    This removes some of the deprecated config elements by making use of a config object.

    I have also removed some methods since keeping them would require making those aware of config; they were already marked for deprecation.

    While doing the work I noticed a bug in the way encoding maps were setup, which I have fixed as well.

    I have also sliced up the file since snowflake.go was growing longer.

  • Generate unique node ID

    Generate unique node ID

    The node ID accepts an int between 0~1023.

    Is there a way to generate distributed unique or sequential node ID?

    Because it's only has 10 bits, it's difficult to generate unique node ID by machine ID or process ID.

  • NodeBits & StepBits change

    NodeBits & StepBits change

    Would it possible that two different nodes created by two different NodeBits produce the same snowflakeID in the same time?

    snowflake.NodeBits = 18
    snowflake.StepBits = 7
    a, _ := snowflake.NewNode(1)
    
    snowflake.NodeBits = 14
    snowflake.StepBits = 8
    b, _ := snowflake.NewNode(2)
    
    //a.Generate() == b.Generate() ?
    
  • feat: allow user custome time precision.

    feat: allow user custome time precision.

    • Allow user custom time precision. We should custom time precision when we can only allocate a few bits for timestamp.

    • GenerateManyallows generate many ids in one call.

    • Add test case for Generate.

  • Data skew in low-concurrency scenarios

    Data skew in low-concurrency scenarios

    Data skew in low-concurrency scenarios

    According to the source code, it will set the step to 0 once the time increased, it will cause a problem when we use the id as the sharding key for sharding strategy. The code affects

    if now == n.time {
    		n.step = (n.step + 1) & n.stepMask
    
    		if n.step == 0 {
    			for now <= n.time {
    				now = time.Since(n.epoch).Nanoseconds() / 1000000
    			}
    		}
    	} else {
    		n.step = 0
    	}
    

    Consider the following circumstance:

    Scenario

    1. table:order_tab_00000000-order_tab_00001000;
    2. An api for user to create new order: create_order;
    3. use the above logic to generate id when QPS is less than 10;

    Impact

    1. After calculating with mod all of the result of id generated will be less than 10, which means most of the records will be stored into order_tab_00000000-order_tab_00000009, it caused data skew;

    Improve

    1. Make the step round by round, and do not set it to 0;

    Code May be referenced

    	n.mu.Lock()
    	now := time.Since(n.epoch).Nanoseconds() / 1000000
    	n.step = (n.step + 1) & n.stepMask
    	if now == n.time && n.step == 0 {
    		for now <= n.time {
    			now = time.Since(n.epoch).Nanoseconds() / 1000000
    		}
    	}
    	n.time = now
    	r := ID((now)<<n.timeShift |
    		(n.node << n.nodeShift) |
    		(n.step),
    	)
    	n.mu.Unlock()
    	return r
    
  • parsing doesn't consider negative numbers

    parsing doesn't consider negative numbers

    using ParseBase58 as an example, values should round-trip properly back to their original value, or error.

    ok := "npL6MjP8Qfc"   //0x7fffffffffffffff
    bad1 := "npL6MjP8Qfd" //0x7fffffffffffffff + 1 // overflows int64 and becomes negative
    bad2 := "JPwcyDCgEuq" //0xffffffffffffffff + 1 // overflows uint64 and wraps back to 1
    
    • in the case of bad1 it parses "successfully" and returns a negative value, which later panics when Base58() cannot convert it back to a string (it doesn't handle negative cases). It should error on parsing, or negatives should be supported throughout (or the base type should be uint64).
    • in the case of bad2 it parses "successfully" and returns a value of 1, which base58() does not encode back to the original number, obviously. it should probably error because the original number is not valid.

    These issues can come up when accepting requests with bad IDs from clients, and they should not be accepted as valid ids.

  • Request: A function to get ID at perticular instance of time in past

    Request: A function to get ID at perticular instance of time in past

    Usecase: snowflake as database primary key. it will be useful to get all records inserted before 1 month.
    It will be great to have a function that:

    1. given a time in past generates ID
    2. ignores node id ?
Snowflake - Simple twitter's snowflake uniquely identifiable descriptors (IDs) format generator for Go

Snowflake Dead simple and fast Twitter's snowflake id generator in Go. Installation go get github.com/HotPotatoC/snowflake Usage Generating a snowflak

Oct 6, 2022
✨ Generate unique IDs (Port of Node package "generate-snowflake" to Golang)

✨ Generate Snowflake Generate unique IDs. Inspired by Twitter's Snowflake system. ?? Installation Initialize your project (go mod init example.com/exa

Feb 11, 2022
Snowflake - An implement of snowflake by go, use atomic instead of mutex

snowflake an implement of snowflake by go, use atomic instead of mutex 雪花算法的一种go

Feb 4, 2022
❄ An Lock Free ID Generator for Golang based on Snowflake Algorithm (Twitter announced).
❄ An Lock Free ID Generator for Golang based on Snowflake Algorithm (Twitter announced).

❄ An Lock Free ID Generator for Golang based on Snowflake Algorithm (Twitter announced).

Dec 14, 2022
A network service for generating unique ID numbers inspired by Twitter's Snowflake.

Hanabira Hanabira is a network service for generating unique ID numbers inspired by Twitter's Snowflake. How to run hanabira-cluster and etcd-cluster

Jan 13, 2022
Golang wrapper for the Snowflake Api.

GoSnowflakeApi GoSnowflakeApi is an api wrapper for the snowflake api written in golang for golang developers. Example package main import ( "fmt

Jul 25, 2021
Snowflake implemented in GO (Golang)

snowflake Snowflake is a fast, goroutine-safe unique ID generator built for distributed systems Key concepts Snowflake Snowflakes are int64s. uint64 i

Oct 27, 2022
Compact, sortable and fast unique IDs with embedded metadata.
Compact, sortable and fast unique IDs with embedded metadata.

A spec for unique IDs in distributed systems based on the Snowflake design, i.e. a coordination-based ID variant. It aims to be friendly to both machi

Dec 22, 2022
K-Sortable Globally Unique IDs

ksuid ksuid is an efficient, comprehensive, battle-tested Go library for generating and parsing a specific kind of globally unique identifier called a

Jan 9, 2023
Generate, encode, and decode UUIDs v1 with fast or cryptographic-quality random node identifier.

A Go package for generating and manipulating UUIDs Generate, encode, and decode UUIDs v1, as defined in RFC 4122, in Go. Project Status v1.1.0 Stable:

Sep 27, 2022
Generate UUID for script's sql

Generate UUID for script's sql Instale o go em sua maquina https://golang.org/doc/install Baixe as seguintes dependecias go get github.com/satori/go.u

Oct 26, 2021
UUID package for Golang

UUID package for Go language This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing

Dec 9, 2021
A UUIDv4 generation package written in go

Goid A Go package to generate V4 UUIDs Documentation The API docs can be viewed here or generated with godoc. Usage An example of generating a v4 UUID

Dec 24, 2022
A UUID package originally forked from github.com/satori/go.uuid

UUID Package uuid provides a pure Go implementation of Universally Unique Identifiers (UUID) variant as defined in RFC-4122. This package supports bot

Dec 30, 2022
Go package for UUIDs based on RFC 4122 and DCE 1.1: Authentication and Security Services.

uuid The uuid package generates and inspects UUIDs based on RFC 4122 and DCE 1.1: Authentication and Security Services. This package is based on the g

Jan 1, 2023
UUID package for Go

UUID package for Go language This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing

Jan 2, 2023
A simple uuid library based on RFC 4122

UUID generator A simple library that generates uuids. Supported versions: version 1 version 3 version 4 version 5 Supported variants: DCE Microsoft Th

Oct 20, 2021
An extremely fast UUID alternative written in golang

Overview WUID is a globally unique number generator, while it is NOT a UUID implementation. WUID is 10-135 times faster than UUID and 4600 times faste

Dec 9, 2022
An extremely fast UUID alternative written in golang

Overview WUID is a globally unique number generator, while it is NOT a UUID implementation. WUID is 10-135 times faster than UUID and 4600 times faste

May 10, 2021