diff --git a/README.md b/README.md index e38715b..3f587ef 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -Sonyflake -========= +# Sonyflake [![GoDoc](https://godoc.org/github.com/sony/sonyflake?status.svg)](http://godoc.org/github.com/sony/sonyflake) [![Go Report Card](https://goreportcard.com/badge/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) diff --git a/v2/example/sonyflake_server.go b/v2/example/sonyflake_server.go index aee4beb..b153346 100644 --- a/v2/example/sonyflake_server.go +++ b/v2/example/sonyflake_server.go @@ -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 diff --git a/v2/sonyflake.go b/v2/sonyflake.go index 67b6939..870c822 100644 --- a/v2/sonyflake.go +++ b/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<= 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<= 1<= 1<> (BitLenSequence + BitLenMachine) -} - -// SequenceNumber returns the sequence number of a Sonyflake ID. -func SequenceNumber(id int64) int { - const maskSequence = int64((1<> BitLenMachine) -} - -// MachineID returns the machine ID of a Sonyflake ID. -func MachineID(id int64) int { - const maskMachine = int64(1<> (sf.bitsSequence + sf.bitsMachine) +} + +func (sf *Sonyflake) sequencePart(id int64) int64 { + maskSequence := int64((1<> sf.bitsMachine +} + +func (sf *Sonyflake) machinePart(id int64) int64 { + maskMachine := int64(1< 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<