mirror of
https://github.com/sony/sonyflake.git
synced 2026-04-24 10:10:42 +00:00
feat(v2): make bit assignment for time/sequence/machine customizable … (#68)
* feat(v2): make bit assignment for time/sequence/machine customizable via Settings; update all logic and tests * gofmt
This commit is contained in:
35
README.md
35
README.md
@@ -1,5 +1,4 @@
|
||||
Sonyflake
|
||||
=========
|
||||
# Sonyflake
|
||||
|
||||
[](http://godoc.org/github.com/sony/sonyflake)
|
||||
[](https://goreportcard.com/report/github.com/sony/sonyflake)
|
||||
@@ -8,7 +7,7 @@ Sonyflake is a distributed unique ID generator inspired by [Twitter's 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
|
||||
By default, a Sonyflake ID is composed of
|
||||
|
||||
39 bits for time in units of 10 msec
|
||||
8 bits for a sequence number
|
||||
@@ -23,15 +22,16 @@ As a result, Sonyflake has the following advantages and disadvantages:
|
||||
However, if you want more generation rate in a single host,
|
||||
you can easily run multiple Sonyflake instances parallelly using goroutines.
|
||||
|
||||
Installation
|
||||
------------
|
||||
In addition, you can adjust the lifetime and generation rate of Sonyflake
|
||||
by customizing the bit assignment and the time unit.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
go get github.com/sony/sonyflake/v2
|
||||
```
|
||||
|
||||
Usage
|
||||
-----
|
||||
## Usage
|
||||
|
||||
The function New creates a new Sonyflake instance.
|
||||
|
||||
@@ -43,6 +43,8 @@ You can configure Sonyflake by the struct Settings:
|
||||
|
||||
```go
|
||||
type Settings struct {
|
||||
BitsSequence int
|
||||
BitsMachineID int
|
||||
TimeUnit time.Duration
|
||||
StartTime time.Time
|
||||
MachineID func() (int, error)
|
||||
@@ -50,9 +52,17 @@ type Settings struct {
|
||||
}
|
||||
```
|
||||
|
||||
- BitsSequence is the bit length of a sequence number.
|
||||
If BitsSequence is 0, the default bit length is used, which is 8.
|
||||
If BitsSequence is 31 or more, an error is returned.
|
||||
|
||||
- BitsMachineID is the bit length of a machine ID.
|
||||
If BitsMachineID is 0, the default bit length is used, which is 16.
|
||||
If BitsMachineID is 31 or more, an error is returned.
|
||||
|
||||
- TimeUnit is the time unit of Sonyflake.
|
||||
If TimeUnit is 0, the default time unit is used, which is 10 msec.
|
||||
TimeUnit must be equal to or greater than 1 msec.
|
||||
TimeUnit must be 1 msec or longer.
|
||||
|
||||
- StartTime is the time since which the Sonyflake time is defined as the elapsed time.
|
||||
If StartTime is 0, the start time of the Sonyflake instance is set to "2025-01-01 00:00:00 +0000 UTC".
|
||||
@@ -66,6 +76,9 @@ type Settings struct {
|
||||
If CheckMachineID returns false, the instance will not be created.
|
||||
If CheckMachineID is nil, no validation is done.
|
||||
|
||||
The bit length of time is calculated by 63 - BitsSequence - BitsMachineID.
|
||||
If it is less than 32, an error is returned.
|
||||
|
||||
In order to get a new unique ID, you just have to call the method NextID.
|
||||
|
||||
```go
|
||||
@@ -75,8 +88,7 @@ func (sf *Sonyflake) NextID() (int64, error)
|
||||
NextID can continue to generate IDs for about 174 years from StartTime by default.
|
||||
But after the Sonyflake time is over the limit, NextID returns an error.
|
||||
|
||||
AWS VPC and Docker
|
||||
------------------
|
||||
## AWS VPC and Docker
|
||||
|
||||
The [awsutil](https://github.com/sony/sonyflake/blob/master/v2/awsutil) package provides
|
||||
the function AmazonEC2MachineID that returns the lower 16-bit private IP address of the Amazon EC2 instance.
|
||||
@@ -91,8 +103,7 @@ In this common case, you can use AmazonEC2MachineID as Settings.MachineID.
|
||||
|
||||
See [example](https://github.com/sony/sonyflake/blob/master/v2/example) that runs Sonyflake on AWS Elastic Beanstalk.
|
||||
|
||||
License
|
||||
-------
|
||||
## License
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ func handler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
body, err := json.Marshal(sonyflake.Decompose(id))
|
||||
body, err := json.Marshal(sf.Decompose(id))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
||||
147
v2/sonyflake.go
147
v2/sonyflake.go
@@ -1,6 +1,6 @@
|
||||
// Package sonyflake implements Sonyflake, a distributed unique ID generator inspired by Twitter's Snowflake.
|
||||
//
|
||||
// A Sonyflake ID is composed of
|
||||
// By default, a Sonyflake ID is composed of
|
||||
//
|
||||
// 39 bits for time in units of 10 msec
|
||||
// 8 bits for a sequence number
|
||||
@@ -16,18 +16,19 @@ import (
|
||||
"github.com/sony/sonyflake/v2/types"
|
||||
)
|
||||
|
||||
// These constants are the bit lengths of Sonyflake ID parts.
|
||||
const (
|
||||
BitLenTime = 39 // bit length of time
|
||||
BitLenSequence = 8 // bit length of sequence number
|
||||
BitLenMachine = 63 - BitLenTime - BitLenSequence // bit length of machine id
|
||||
)
|
||||
|
||||
// Settings configures Sonyflake:
|
||||
//
|
||||
// BitsSequence is the bit length of a sequence number.
|
||||
// If BitsSequence is 0, the default bit length is used, which is 8.
|
||||
// If BitsSequence is 31 or more, an error is returned.
|
||||
//
|
||||
// BitsMachineID is the bit length of a machine ID.
|
||||
// If BitsMachineID is 0, the default bit length is used, which is 16.
|
||||
// If BitsMachineID is 31 or more, an error is returned.
|
||||
//
|
||||
// TimeUnit is the time unit of Sonyflake.
|
||||
// If TimeUnit is 0, the default time unit is used, which is 10 msec.
|
||||
// TimeUnit must be equal to or greater than 1 msec.
|
||||
// TimeUnit must be 1 msec or longer.
|
||||
//
|
||||
// StartTime is the time since which the Sonyflake time is defined as the elapsed time.
|
||||
// If StartTime is 0, the start time of the Sonyflake instance is set to "2025-01-01 00:00:00 +0000 UTC".
|
||||
@@ -40,7 +41,12 @@ const (
|
||||
// CheckMachineID validates the uniqueness of a machine ID.
|
||||
// If CheckMachineID returns false, the instance will not be created.
|
||||
// If CheckMachineID is nil, no validation is done.
|
||||
//
|
||||
// The bit length of time is calculated by 63 - BitsSequence - BitsMachineID.
|
||||
// If it is less than 32, an error is returned.
|
||||
type Settings struct {
|
||||
BitsSequence int
|
||||
BitsMachineID int
|
||||
TimeUnit time.Duration
|
||||
StartTime time.Time
|
||||
MachineID func() (int, error)
|
||||
@@ -49,46 +55,88 @@ type Settings struct {
|
||||
|
||||
// Sonyflake is a distributed unique ID generator.
|
||||
type Sonyflake struct {
|
||||
mutex *sync.Mutex
|
||||
mutex *sync.Mutex
|
||||
|
||||
bitsTime int
|
||||
bitsSequence int
|
||||
bitsMachine int
|
||||
|
||||
timeUnit int64
|
||||
startTime int64
|
||||
elapsedTime int64
|
||||
sequence int
|
||||
machine int
|
||||
|
||||
sequence int
|
||||
machine int
|
||||
}
|
||||
|
||||
var (
|
||||
ErrStartTimeAhead = errors.New("start time is ahead of now")
|
||||
ErrNoPrivateAddress = errors.New("no private ip address")
|
||||
ErrOverTimeLimit = errors.New("over the time limit")
|
||||
ErrInvalidMachineID = errors.New("invalid machine id")
|
||||
ErrInvalidTimeUnit = errors.New("invalid time unit")
|
||||
ErrInvalidBitsTime = errors.New("bit length for time must be 32 or more")
|
||||
ErrInvalidBitsSequence = errors.New("invalid bit length for sequence number")
|
||||
ErrInvalidBitsMachineID = errors.New("invalid bit length for machine id")
|
||||
ErrInvalidTimeUnit = errors.New("invalid time unit")
|
||||
ErrInvalidMachineID = errors.New("invalid machine id")
|
||||
ErrStartTimeAhead = errors.New("start time is ahead of now")
|
||||
ErrOverTimeLimit = errors.New("over the time limit")
|
||||
ErrNoPrivateAddress = errors.New("no private ip address")
|
||||
)
|
||||
|
||||
const defaultTimeUnit = 1e7 // nsec, i.e. 10 msec
|
||||
const (
|
||||
defaultTimeUnit = 1e7 // nsec, i.e. 10 msec
|
||||
|
||||
defaultBitsTime = 39
|
||||
defaultBitsSequence = 8
|
||||
defaultBitsMachine = 16
|
||||
)
|
||||
|
||||
var defaultInterfaceAddrs = net.InterfaceAddrs
|
||||
|
||||
// New returns a new Sonyflake configured with the given Settings.
|
||||
// New returns an error in the following cases:
|
||||
// - Settings.BitsSequence is less than 0 or greater than 30.
|
||||
// - Settings.BitsMachineID is less than 0 or greater than 30.
|
||||
// - Settings.BitsSequence + Settings.BitsMachineID is 32 or more.
|
||||
// - Settings.TimeUnit is less than 1 msec.
|
||||
// - Settings.StartTime is ahead of the current time.
|
||||
// - Settings.MachineID returns an error.
|
||||
// - Settings.CheckMachineID returns false.
|
||||
func New(st Settings) (*Sonyflake, error) {
|
||||
if st.BitsSequence < 0 || st.BitsSequence > 30 {
|
||||
return nil, ErrInvalidBitsSequence
|
||||
}
|
||||
if st.BitsMachineID < 0 || st.BitsMachineID > 30 {
|
||||
return nil, ErrInvalidBitsMachineID
|
||||
}
|
||||
if st.TimeUnit < 0 || (st.TimeUnit > 0 && st.TimeUnit < time.Millisecond) {
|
||||
return nil, ErrInvalidTimeUnit
|
||||
}
|
||||
if st.StartTime.After(time.Now()) {
|
||||
return nil, ErrStartTimeAhead
|
||||
}
|
||||
|
||||
sf := new(Sonyflake)
|
||||
sf.mutex = new(sync.Mutex)
|
||||
sf.sequence = 1<<BitLenSequence - 1
|
||||
|
||||
if st.BitsSequence == 0 {
|
||||
sf.bitsSequence = defaultBitsSequence
|
||||
} else {
|
||||
sf.bitsSequence = st.BitsSequence
|
||||
}
|
||||
|
||||
if st.BitsMachineID == 0 {
|
||||
sf.bitsMachine = defaultBitsMachine
|
||||
} else {
|
||||
sf.bitsMachine = st.BitsMachineID
|
||||
}
|
||||
|
||||
sf.bitsTime = 63 - sf.bitsSequence - sf.bitsMachine
|
||||
if sf.bitsTime < 32 {
|
||||
return nil, ErrInvalidBitsTime
|
||||
}
|
||||
|
||||
if st.TimeUnit == 0 {
|
||||
sf.timeUnit = defaultTimeUnit
|
||||
} else if st.TimeUnit >= time.Millisecond {
|
||||
sf.timeUnit = int64(st.TimeUnit)
|
||||
} else {
|
||||
return nil, ErrInvalidTimeUnit
|
||||
sf.timeUnit = int64(st.TimeUnit)
|
||||
}
|
||||
|
||||
if st.StartTime.IsZero() {
|
||||
@@ -97,6 +145,8 @@ func New(st Settings) (*Sonyflake, error) {
|
||||
sf.startTime = sf.toInternalTime(st.StartTime)
|
||||
}
|
||||
|
||||
sf.sequence = 1<<sf.bitsSequence - 1
|
||||
|
||||
var err error
|
||||
if st.MachineID == nil {
|
||||
sf.machine, err = lower16BitPrivateIP(defaultInterfaceAddrs)
|
||||
@@ -117,7 +167,7 @@ func New(st Settings) (*Sonyflake, error) {
|
||||
// NextID generates a next unique ID as int64.
|
||||
// After the Sonyflake time overflows, NextID returns an error.
|
||||
func (sf *Sonyflake) NextID() (int64, error) {
|
||||
const maskSequence = 1<<BitLenSequence - 1
|
||||
maskSequence := 1<<sf.bitsSequence - 1
|
||||
|
||||
sf.mutex.Lock()
|
||||
defer sf.mutex.Unlock()
|
||||
@@ -153,12 +203,12 @@ func (sf *Sonyflake) sleep(overtime int64) {
|
||||
}
|
||||
|
||||
func (sf *Sonyflake) toID() (int64, error) {
|
||||
if sf.elapsedTime >= 1<<BitLenTime {
|
||||
if sf.elapsedTime >= 1<<sf.bitsTime {
|
||||
return 0, ErrOverTimeLimit
|
||||
}
|
||||
|
||||
return sf.elapsedTime<<(BitLenSequence+BitLenMachine) |
|
||||
int64(sf.sequence)<<BitLenMachine |
|
||||
return sf.elapsedTime<<(sf.bitsSequence+sf.bitsMachine) |
|
||||
int64(sf.sequence)<<sf.bitsMachine |
|
||||
int64(sf.machine), nil
|
||||
}
|
||||
|
||||
@@ -198,35 +248,32 @@ func lower16BitPrivateIP(interfaceAddrs types.InterfaceAddrs) (int, error) {
|
||||
}
|
||||
|
||||
func (sf *Sonyflake) ToTime(id int64) time.Time {
|
||||
return time.Unix(0, (sf.startTime+Time(id))*sf.timeUnit)
|
||||
}
|
||||
|
||||
// Time returns the Sonyflake time when the given ID was generated.
|
||||
func Time(id int64) int64 {
|
||||
return id >> (BitLenSequence + BitLenMachine)
|
||||
}
|
||||
|
||||
// SequenceNumber returns the sequence number of a Sonyflake ID.
|
||||
func SequenceNumber(id int64) int {
|
||||
const maskSequence = int64((1<<BitLenSequence - 1) << BitLenMachine)
|
||||
return int((id & maskSequence) >> BitLenMachine)
|
||||
}
|
||||
|
||||
// MachineID returns the machine ID of a Sonyflake ID.
|
||||
func MachineID(id int64) int {
|
||||
const maskMachine = int64(1<<BitLenMachine - 1)
|
||||
return int(id & maskMachine)
|
||||
return time.Unix(0, (sf.startTime+sf.timePart(id))*sf.timeUnit)
|
||||
}
|
||||
|
||||
// Decompose returns a set of Sonyflake ID parts.
|
||||
func Decompose(id int64) map[string]int64 {
|
||||
time := Time(id)
|
||||
sequence := SequenceNumber(id)
|
||||
machine := MachineID(id)
|
||||
func (sf *Sonyflake) Decompose(id int64) map[string]int64 {
|
||||
time := sf.timePart(id)
|
||||
sequence := sf.sequencePart(id)
|
||||
machine := sf.machinePart(id)
|
||||
return map[string]int64{
|
||||
"id": id,
|
||||
"time": time,
|
||||
"sequence": int64(sequence),
|
||||
"machine": int64(machine),
|
||||
"sequence": sequence,
|
||||
"machine": machine,
|
||||
}
|
||||
}
|
||||
|
||||
func (sf *Sonyflake) timePart(id int64) int64 {
|
||||
return id >> (sf.bitsSequence + sf.bitsMachine)
|
||||
}
|
||||
|
||||
func (sf *Sonyflake) sequencePart(id int64) int64 {
|
||||
maskSequence := int64((1<<sf.bitsSequence - 1) << sf.bitsMachine)
|
||||
return (id & maskSequence) >> sf.bitsMachine
|
||||
}
|
||||
|
||||
func (sf *Sonyflake) machinePart(id int64) int64 {
|
||||
maskMachine := int64(1<<sf.bitsMachine - 1)
|
||||
return id & maskMachine
|
||||
}
|
||||
|
||||
@@ -20,6 +20,28 @@ func TestNew(t *testing.T) {
|
||||
settings Settings
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "invalid bit length for time",
|
||||
settings: Settings{
|
||||
BitsSequence: 16,
|
||||
BitsMachineID: 16,
|
||||
},
|
||||
err: ErrInvalidBitsTime,
|
||||
},
|
||||
{
|
||||
name: "invalid bit length for sequence number",
|
||||
settings: Settings{
|
||||
BitsSequence: -1,
|
||||
},
|
||||
err: ErrInvalidBitsSequence,
|
||||
},
|
||||
{
|
||||
name: "invalid bit length for machine id",
|
||||
settings: Settings{
|
||||
BitsMachineID: 31,
|
||||
},
|
||||
err: ErrInvalidBitsMachineID,
|
||||
},
|
||||
{
|
||||
name: "invalid time unit",
|
||||
settings: Settings{
|
||||
@@ -105,23 +127,23 @@ func TestNextID(t *testing.T) {
|
||||
|
||||
id := nextID(t, sf)
|
||||
|
||||
actualTime := Time(id)
|
||||
actualTime := sf.timePart(id)
|
||||
if actualTime < sleepTime || actualTime > sleepTime+1 {
|
||||
t.Errorf("unexpected time: %d", actualTime)
|
||||
}
|
||||
|
||||
actualSequence := SequenceNumber(id)
|
||||
actualSequence := sf.sequencePart(id)
|
||||
if actualSequence != 0 {
|
||||
t.Errorf("unexpected sequence: %d", actualSequence)
|
||||
}
|
||||
|
||||
actualMachine := MachineID(id)
|
||||
if actualMachine != defaultMachineID(t) {
|
||||
actualMachine := sf.machinePart(id)
|
||||
if actualMachine != int64(defaultMachineID(t)) {
|
||||
t.Errorf("unexpected machine: %d", actualMachine)
|
||||
}
|
||||
|
||||
fmt.Println("sonyflake id:", id)
|
||||
fmt.Println("decompose:", Decompose(id))
|
||||
fmt.Println("decompose:", sf.Decompose(id))
|
||||
}
|
||||
|
||||
func TestNextID_InSequence(t *testing.T) {
|
||||
@@ -151,7 +173,7 @@ func TestNextID_InSequence(t *testing.T) {
|
||||
}
|
||||
lastID = id
|
||||
|
||||
parts := Decompose(id)
|
||||
parts := sf.Decompose(id)
|
||||
|
||||
actualTime := parts["time"]
|
||||
overtime := startTime + actualTime - currentTime
|
||||
@@ -170,7 +192,7 @@ func TestNextID_InSequence(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
if maxSeq != 1<<BitLenSequence-1 {
|
||||
if maxSeq != 1<<sf.bitsSequence-1 {
|
||||
t.Errorf("unexpected max sequence: %d", maxSeq)
|
||||
}
|
||||
fmt.Println("max sequence:", maxSeq)
|
||||
|
||||
Reference in New Issue
Block a user