9 Commits

Author SHA1 Message Date
Yoshiyuki Mineo
3719d006ac Add a note (#36) 2022-08-13 00:23:02 +09:00
Yoshiyuki Mineo
90e18212ad Add functions to get ID elements (#35)
* Refactoring

* Add functions to get ID elements
2022-08-12 18:42:10 +09:00
Yoshiyuki Mineo
809c515cc5 Introduce GitHub Actions (#34)
* Introduce GitHub Actions

* gofmt
2022-08-12 18:38:40 +09:00
Osamu TONOMORI
848d664cee Make testing simple (#20)
* Make testing simpler

* Remove testing deps

* Simplemize tests
2020-08-27 10:17:19 +09:00
Yoshiyuki Mineo
60e9d38e92 Merge pull request #19 from osamingo/set-go-stable-versions
Set Go stable versions to .travis.yml
2020-08-18 16:53:08 +09:00
Osamu TONOMORI
f68244fede Set Go stable versions 2020-08-10 16:05:22 +09:00
Osamu TONOMORI
963f058659 Remove sudo setting
Setting `sudo: false` is not recommended.
via/
https://blog.travis-ci.com/2018-11-19-required-linux-infrastructure-migration
2020-08-10 16:03:44 +09:00
Yoshiyuki Mineo
59cd942daa Update README.md 2019-10-07 16:50:43 +09:00
Yoshiyuki Mineo
3ffd8c4254 Update README.md 2019-10-07 16:40:37 +09:00
7 changed files with 88 additions and 52 deletions

24
.github/workflows/test.yml vendored Normal file
View 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

View File

@@ -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

View File

@@ -2,17 +2,27 @@ Sonyflake
=========
[![GoDoc](https://godoc.org/github.com/sony/sonyflake?status.svg)](http://godoc.org/github.com/sony/sonyflake)
[![Build Status](https://travis-ci.org/sony/sonyflake.svg?branch=master)](https://travis-ci.org/sony/sonyflake)
[![Coverage Status](https://coveralls.io/repos/sony/sonyflake/badge.svg?branch=master&service=github)](https://coveralls.io/github/sony/sonyflake?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/sony/sonyflake)](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
View File

@@ -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
View File

@@ -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=

View File

@@ -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,

View File

@@ -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")
}
}