mirror of
https://github.com/sony/sonyflake.git
synced 2026-02-02 22:52:01 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3719d006ac | ||
|
|
90e18212ad | ||
|
|
809c515cc5 | ||
|
|
848d664cee | ||
|
|
60e9d38e92 | ||
|
|
f68244fede | ||
|
|
963f058659 | ||
|
|
59cd942daa | ||
|
|
3ffd8c4254 |
24
.github/workflows/test.yml
vendored
Normal file
24
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
on: [push, pull_request]
|
||||
name: Test
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.18.x, 1.19.x]
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{matrix.os}}
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{matrix.go-version}}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: gofmt
|
||||
run: test -z "`gofmt -l .`"
|
||||
- name: golint
|
||||
run: test -z "`golint ./...`"
|
||||
- name: go test
|
||||
run: go test -v ./...
|
||||
- name: Build example
|
||||
run: cd example && ./linux64_build.sh
|
||||
14
.travis.yml
14
.travis.yml
@@ -1,14 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
sudo: false
|
||||
before_install:
|
||||
- go get -u golang.org/x/lint/golint
|
||||
- go get github.com/axw/gocov/gocov
|
||||
- go get github.com/mattn/goveralls
|
||||
script:
|
||||
- test -z "`gofmt -l .`"
|
||||
- test -z "`golint ./...`"
|
||||
- $GOPATH/bin/goveralls -service=travis-ci
|
||||
- cd example && ./linux64_build.sh
|
||||
18
README.md
18
README.md
@@ -2,17 +2,27 @@ Sonyflake
|
||||
=========
|
||||
|
||||
[](http://godoc.org/github.com/sony/sonyflake)
|
||||
[](https://travis-ci.org/sony/sonyflake)
|
||||
[](https://coveralls.io/github/sony/sonyflake?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/sony/sonyflake)
|
||||
|
||||
Sonyflake is a distributed unique ID generator inspired by [Twitter's Snowflake](https://blog.twitter.com/2010/announcing-snowflake).
|
||||
|
||||
Sonyflake focuses on lifetime and performance on many host/core environment.
|
||||
So it has a different bit assignment from Snowflake.
|
||||
A Sonyflake ID is composed of
|
||||
|
||||
39 bits for time in units of 10 msec
|
||||
8 bits for a sequence number
|
||||
16 bits for a machine id
|
||||
|
||||
As a result, Sonyflake has the following advantages and disadvantages:
|
||||
|
||||
- The lifetime (174 years) is longer than that of Snowflake (69 years)
|
||||
- It can work in more distributed machines (2^16) than Snowflake (2^10)
|
||||
- It can generate 2^8 IDs per 10 msec at most in a single machine/thread (slower than Snowflake)
|
||||
|
||||
However, if you want more generation rate in a single host,
|
||||
you can easily run multiple Sonyflake ID generators concurrently using goroutines.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
@@ -61,6 +71,10 @@ func (sf *Sonyflake) NextID() (uint64, error)
|
||||
NextID can continue to generate IDs for about 174 years from StartTime.
|
||||
But after the Sonyflake time is over the limit, NextID returns an error.
|
||||
|
||||
> **Note:**
|
||||
> Sonyflake currently does not use the most significant bit of IDs,
|
||||
> so you can convert Sonyflake IDs from `uint64` to `int64` safely.
|
||||
|
||||
AWS VPC and Docker
|
||||
------------------
|
||||
|
||||
|
||||
4
go.mod
4
go.mod
@@ -1,5 +1,3 @@
|
||||
module github.com/sony/sonyflake
|
||||
|
||||
go 1.12
|
||||
|
||||
require github.com/deckarep/golang-set v1.7.1
|
||||
go 1.13
|
||||
|
||||
2
go.sum
2
go.sum
@@ -1,2 +0,0 @@
|
||||
github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
|
||||
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||
|
||||
41
sonyflake.go
41
sonyflake.go
@@ -1,9 +1,10 @@
|
||||
// Package sonyflake implements Sonyflake, a distributed unique ID generator inspired by Twitter's Snowflake.
|
||||
//
|
||||
// A Sonyflake ID is composed of
|
||||
// 39 bits for time in units of 10 msec
|
||||
// 8 bits for a sequence number
|
||||
// 16 bits for a machine id
|
||||
//
|
||||
// 39 bits for time in units of 10 msec
|
||||
// 8 bits for a sequence number
|
||||
// 16 bits for a machine id
|
||||
package sonyflake
|
||||
|
||||
import (
|
||||
@@ -116,8 +117,8 @@ func currentElapsedTime(startTime int64) int64 {
|
||||
}
|
||||
|
||||
func sleepTime(overtime int64) time.Duration {
|
||||
return time.Duration(overtime)*10*time.Millisecond -
|
||||
time.Duration(time.Now().UTC().UnixNano()%sonyflakeTimeUnit)*time.Nanosecond
|
||||
return time.Duration(overtime*sonyflakeTimeUnit) -
|
||||
time.Duration(time.Now().UTC().UnixNano()%sonyflakeTimeUnit)
|
||||
}
|
||||
|
||||
func (sf *Sonyflake) toID() (uint64, error) {
|
||||
@@ -164,15 +165,33 @@ func lower16BitPrivateIP() (uint16, error) {
|
||||
return uint16(ip[2])<<8 + uint16(ip[3]), nil
|
||||
}
|
||||
|
||||
// ElapsedTime returns the elapsed time when the given Sonyflake ID was generated.
|
||||
func ElapsedTime(id uint64) time.Duration {
|
||||
return time.Duration(elapsedTime(id) * sonyflakeTimeUnit)
|
||||
}
|
||||
|
||||
func elapsedTime(id uint64) uint64 {
|
||||
return id >> (BitLenSequence + BitLenMachineID)
|
||||
}
|
||||
|
||||
// SequenceNumber returns the sequence number of a Sonyflake ID.
|
||||
func SequenceNumber(id uint64) uint64 {
|
||||
const maskSequence = uint64((1<<BitLenSequence - 1) << BitLenMachineID)
|
||||
return id & maskSequence >> BitLenMachineID
|
||||
}
|
||||
|
||||
// MachineID returns the machine ID of a Sonyflake ID.
|
||||
func MachineID(id uint64) uint64 {
|
||||
const maskMachineID = uint64(1<<BitLenMachineID - 1)
|
||||
return id & maskMachineID
|
||||
}
|
||||
|
||||
// Decompose returns a set of Sonyflake ID parts.
|
||||
func Decompose(id uint64) map[string]uint64 {
|
||||
const maskSequence = uint64((1<<BitLenSequence - 1) << BitLenMachineID)
|
||||
const maskMachineID = uint64(1<<BitLenMachineID - 1)
|
||||
|
||||
msb := id >> 63
|
||||
time := id >> (BitLenSequence + BitLenMachineID)
|
||||
sequence := id & maskSequence >> BitLenMachineID
|
||||
machineID := id & maskMachineID
|
||||
time := elapsedTime(id)
|
||||
sequence := SequenceNumber(id)
|
||||
machineID := MachineID(id)
|
||||
return map[string]uint64{
|
||||
"id": id,
|
||||
"msb": msb,
|
||||
|
||||
@@ -5,8 +5,6 @@ import (
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/deckarep/golang-set"
|
||||
)
|
||||
|
||||
var sf *Sonyflake
|
||||
@@ -38,34 +36,28 @@ func nextID(t *testing.T) uint64 {
|
||||
}
|
||||
|
||||
func TestSonyflakeOnce(t *testing.T) {
|
||||
sleepTime := uint64(50)
|
||||
time.Sleep(time.Duration(sleepTime) * 10 * time.Millisecond)
|
||||
sleepTime := time.Duration(50 * sonyflakeTimeUnit)
|
||||
time.Sleep(sleepTime)
|
||||
|
||||
id := nextID(t)
|
||||
parts := Decompose(id)
|
||||
|
||||
actualMSB := parts["msb"]
|
||||
if actualMSB != 0 {
|
||||
t.Errorf("unexpected msb: %d", actualMSB)
|
||||
}
|
||||
|
||||
actualTime := parts["time"]
|
||||
if actualTime < sleepTime || actualTime > sleepTime+1 {
|
||||
actualTime := ElapsedTime(id)
|
||||
if actualTime < sleepTime || actualTime > sleepTime+sonyflakeTimeUnit {
|
||||
t.Errorf("unexpected time: %d", actualTime)
|
||||
}
|
||||
|
||||
actualSequence := parts["sequence"]
|
||||
actualSequence := SequenceNumber(id)
|
||||
if actualSequence != 0 {
|
||||
t.Errorf("unexpected sequence: %d", actualSequence)
|
||||
}
|
||||
|
||||
actualMachineID := parts["machine-id"]
|
||||
actualMachineID := MachineID(id)
|
||||
if actualMachineID != machineID {
|
||||
t.Errorf("unexpected machine id: %d", actualMachineID)
|
||||
}
|
||||
|
||||
fmt.Println("sonyflake id:", id)
|
||||
fmt.Println("decompose:", parts)
|
||||
fmt.Println("decompose:", Decompose(id))
|
||||
}
|
||||
|
||||
func currentTime() int64 {
|
||||
@@ -139,16 +131,15 @@ func TestSonyflakeInParallel(t *testing.T) {
|
||||
go generate()
|
||||
}
|
||||
|
||||
set := mapset.NewSet()
|
||||
set := make(map[uint64]struct{})
|
||||
for i := 0; i < numID*numGenerator; i++ {
|
||||
id := <-consumer
|
||||
if set.Contains(id) {
|
||||
if _, ok := set[id]; ok {
|
||||
t.Fatal("duplicated id")
|
||||
} else {
|
||||
set.Add(id)
|
||||
}
|
||||
set[id] = struct{}{}
|
||||
}
|
||||
fmt.Println("number of id:", set.Cardinality())
|
||||
fmt.Println("number of id:", len(set))
|
||||
}
|
||||
|
||||
func TestNilSonyflake(t *testing.T) {
|
||||
@@ -190,3 +181,9 @@ func TestNextIDError(t *testing.T) {
|
||||
t.Errorf("time is not over")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSonyflakeTimeUnit(t *testing.T) {
|
||||
if time.Duration(sonyflakeTimeUnit) != 10*time.Millisecond {
|
||||
t.Errorf("unexpected time unit")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user