Skip to content

Commit 227b5fe

Browse files
feat(v2): make bit assignment for time/sequence/machine customizable via Settings; update all logic and tests
1 parent 357b2ee commit 227b5fe

4 files changed

Lines changed: 150 additions & 70 deletions

File tree

README.md

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
Sonyflake
2-
=========
1+
# Sonyflake
32

43
[![GoDoc](https://godoc.org/github.com/sony/sonyflake?status.svg)](http://godoc.org/github.com/sony/sonyflake)
54
[![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]
87

98
Sonyflake focuses on lifetime and performance on many host/core environment.
109
So it has a different bit assignment from Snowflake.
11-
A Sonyflake ID is composed of
10+
By default, a Sonyflake ID is composed of
1211

1312
39 bits for time in units of 10 msec
1413
8 bits for a sequence number
@@ -23,15 +22,16 @@ As a result, Sonyflake has the following advantages and disadvantages:
2322
However, if you want more generation rate in a single host,
2423
you can easily run multiple Sonyflake instances parallelly using goroutines.
2524

26-
Installation
27-
------------
25+
In addition, you can adjust the lifetime and generation rate of Sonyflake
26+
by customizing the bit assignment and the time unit.
27+
28+
## Installation
2829

2930
```
3031
go get github.com/sony/sonyflake/v2
3132
```
3233

33-
Usage
34-
-----
34+
## Usage
3535

3636
The function New creates a new Sonyflake instance.
3737

@@ -43,16 +43,26 @@ You can configure Sonyflake by the struct Settings:
4343

4444
```go
4545
type Settings struct {
46+
BitsSequence int
47+
BitsMachineID int
4648
TimeUnit time.Duration
4749
StartTime time.Time
4850
MachineID func() (int, error)
4951
CheckMachineID func(int) bool
5052
}
5153
```
5254

55+
- BitsSequence is the bit length of a sequence number.
56+
If BitsSequence is 0, the default bit length is used, which is 8.
57+
If BitsSequence is 31 or more, an error is returned.
58+
59+
- BitsMachineID is the bit length of a machine ID.
60+
If BitsMachineID is 0, the default bit length is used, which is 16.
61+
If BitsMachineID is 31 or more, an error is returned.
62+
5363
- TimeUnit is the time unit of Sonyflake.
5464
If TimeUnit is 0, the default time unit is used, which is 10 msec.
55-
TimeUnit must be equal to or greater than 1 msec.
65+
TimeUnit must be 1 msec or longer.
5666

5767
- StartTime is the time since which the Sonyflake time is defined as the elapsed time.
5868
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 {
6676
If CheckMachineID returns false, the instance will not be created.
6777
If CheckMachineID is nil, no validation is done.
6878

79+
The bit length of time is calculated by 63 - BitsSequence - BitsMachineID.
80+
If it is less than 32, an error is returned.
81+
6982
In order to get a new unique ID, you just have to call the method NextID.
7083

7184
```go
@@ -75,8 +88,7 @@ func (sf *Sonyflake) NextID() (int64, error)
7588
NextID can continue to generate IDs for about 174 years from StartTime by default.
7689
But after the Sonyflake time is over the limit, NextID returns an error.
7790

78-
AWS VPC and Docker
79-
------------------
91+
## AWS VPC and Docker
8092

8193
The [awsutil](https://github.com/sony/sonyflake/blob/master/v2/awsutil) package provides
8294
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.
91103

92104
See [example](https://github.com/sony/sonyflake/blob/master/v2/example) that runs Sonyflake on AWS Elastic Beanstalk.
93105

94-
License
95-
-------
106+
## License
96107

97108
The MIT License (MIT)
98109

v2/example/sonyflake_server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func handler(w http.ResponseWriter, r *http.Request) {
2828
return
2929
}
3030

31-
body, err := json.Marshal(sonyflake.Decompose(id))
31+
body, err := json.Marshal(sf.Decompose(id))
3232
if err != nil {
3333
http.Error(w, err.Error(), http.StatusInternalServerError)
3434
return

v2/sonyflake.go

Lines changed: 97 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Package sonyflake implements Sonyflake, a distributed unique ID generator inspired by Twitter's Snowflake.
22
//
3-
// A Sonyflake ID is composed of
3+
// By default, a Sonyflake ID is composed of
44
//
55
// 39 bits for time in units of 10 msec
66
// 8 bits for a sequence number
@@ -16,18 +16,19 @@ import (
1616
"github.com/sony/sonyflake/v2/types"
1717
)
1818

19-
// These constants are the bit lengths of Sonyflake ID parts.
20-
const (
21-
BitLenTime = 39 // bit length of time
22-
BitLenSequence = 8 // bit length of sequence number
23-
BitLenMachine = 63 - BitLenTime - BitLenSequence // bit length of machine id
24-
)
25-
2619
// Settings configures Sonyflake:
2720
//
21+
// BitsSequence is the bit length of a sequence number.
22+
// If BitsSequence is 0, the default bit length is used, which is 8.
23+
// If BitsSequence is 31 or more, an error is returned.
24+
//
25+
// BitsMachineID is the bit length of a machine ID.
26+
// If BitsMachineID is 0, the default bit length is used, which is 16.
27+
// If BitsMachineID is 31 or more, an error is returned.
28+
//
2829
// TimeUnit is the time unit of Sonyflake.
2930
// If TimeUnit is 0, the default time unit is used, which is 10 msec.
30-
// TimeUnit must be equal to or greater than 1 msec.
31+
// TimeUnit must be 1 msec or longer.
3132
//
3233
// StartTime is the time since which the Sonyflake time is defined as the elapsed time.
3334
// 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 (
4041
// CheckMachineID validates the uniqueness of a machine ID.
4142
// If CheckMachineID returns false, the instance will not be created.
4243
// If CheckMachineID is nil, no validation is done.
44+
//
45+
// The bit length of time is calculated by 63 - BitsSequence - BitsMachineID.
46+
// If it is less than 32, an error is returned.
4347
type Settings struct {
48+
BitsSequence int
49+
BitsMachineID int
4450
TimeUnit time.Duration
4551
StartTime time.Time
4652
MachineID func() (int, error)
@@ -49,46 +55,88 @@ type Settings struct {
4955

5056
// Sonyflake is a distributed unique ID generator.
5157
type Sonyflake struct {
52-
mutex *sync.Mutex
58+
mutex *sync.Mutex
59+
60+
bitsTime int
61+
bitsSequence int
62+
bitsMachine int
63+
5364
timeUnit int64
5465
startTime int64
5566
elapsedTime int64
56-
sequence int
57-
machine int
67+
68+
sequence int
69+
machine int
5870
}
5971

6072
var (
61-
ErrStartTimeAhead = errors.New("start time is ahead of now")
62-
ErrNoPrivateAddress = errors.New("no private ip address")
63-
ErrOverTimeLimit = errors.New("over the time limit")
64-
ErrInvalidMachineID = errors.New("invalid machine id")
65-
ErrInvalidTimeUnit = errors.New("invalid time unit")
73+
ErrInvalidBitsTime = errors.New("bit length for time must be 32 or more")
74+
ErrInvalidBitsSequence = errors.New("invalid bit length for sequence number")
75+
ErrInvalidBitsMachineID = errors.New("invalid bit length for machine id")
76+
ErrInvalidTimeUnit = errors.New("invalid time unit")
77+
ErrInvalidMachineID = errors.New("invalid machine id")
78+
ErrStartTimeAhead = errors.New("start time is ahead of now")
79+
ErrOverTimeLimit = errors.New("over the time limit")
80+
ErrNoPrivateAddress = errors.New("no private ip address")
6681
)
6782

68-
const defaultTimeUnit = 1e7 // nsec, i.e. 10 msec
83+
const (
84+
defaultTimeUnit = 1e7 // nsec, i.e. 10 msec
85+
86+
defaultBitsTime = 39
87+
defaultBitsSequence = 8
88+
defaultBitsMachine = 16
89+
)
6990

7091
var defaultInterfaceAddrs = net.InterfaceAddrs
7192

7293
// New returns a new Sonyflake configured with the given Settings.
7394
// New returns an error in the following cases:
95+
// - Settings.BitsSequence is less than 0 or greater than 30.
96+
// - Settings.BitsMachineID is less than 0 or greater than 30.
97+
// - Settings.BitsSequence + Settings.BitsMachineID is 32 or more.
98+
// - Settings.TimeUnit is less than 1 msec.
7499
// - Settings.StartTime is ahead of the current time.
75100
// - Settings.MachineID returns an error.
76101
// - Settings.CheckMachineID returns false.
77102
func New(st Settings) (*Sonyflake, error) {
103+
if st.BitsSequence < 0 || st.BitsSequence > 30 {
104+
return nil, ErrInvalidBitsSequence
105+
}
106+
if st.BitsMachineID < 0 || st.BitsMachineID > 30 {
107+
return nil, ErrInvalidBitsMachineID
108+
}
109+
if st.TimeUnit < 0 || (st.TimeUnit > 0 && st.TimeUnit < time.Millisecond) {
110+
return nil, ErrInvalidTimeUnit
111+
}
78112
if st.StartTime.After(time.Now()) {
79113
return nil, ErrStartTimeAhead
80114
}
81115

82116
sf := new(Sonyflake)
83117
sf.mutex = new(sync.Mutex)
84-
sf.sequence = 1<<BitLenSequence - 1
118+
119+
if st.BitsSequence == 0 {
120+
sf.bitsSequence = defaultBitsSequence
121+
} else {
122+
sf.bitsSequence = st.BitsSequence
123+
}
124+
125+
if st.BitsMachineID == 0 {
126+
sf.bitsMachine = defaultBitsMachine
127+
} else {
128+
sf.bitsMachine = st.BitsMachineID
129+
}
130+
131+
sf.bitsTime = 63 - sf.bitsSequence - sf.bitsMachine
132+
if sf.bitsTime < 32 {
133+
return nil, ErrInvalidBitsTime
134+
}
85135

86136
if st.TimeUnit == 0 {
87137
sf.timeUnit = defaultTimeUnit
88-
} else if st.TimeUnit >= time.Millisecond {
89-
sf.timeUnit = int64(st.TimeUnit)
90138
} else {
91-
return nil, ErrInvalidTimeUnit
139+
sf.timeUnit = int64(st.TimeUnit)
92140
}
93141

94142
if st.StartTime.IsZero() {
@@ -97,6 +145,8 @@ func New(st Settings) (*Sonyflake, error) {
97145
sf.startTime = sf.toInternalTime(st.StartTime)
98146
}
99147

148+
sf.sequence = 1<<sf.bitsSequence - 1
149+
100150
var err error
101151
if st.MachineID == nil {
102152
sf.machine, err = lower16BitPrivateIP(defaultInterfaceAddrs)
@@ -117,7 +167,7 @@ func New(st Settings) (*Sonyflake, error) {
117167
// NextID generates a next unique ID as int64.
118168
// After the Sonyflake time overflows, NextID returns an error.
119169
func (sf *Sonyflake) NextID() (int64, error) {
120-
const maskSequence = 1<<BitLenSequence - 1
170+
maskSequence := 1<<sf.bitsSequence - 1
121171

122172
sf.mutex.Lock()
123173
defer sf.mutex.Unlock()
@@ -153,12 +203,12 @@ func (sf *Sonyflake) sleep(overtime int64) {
153203
}
154204

155205
func (sf *Sonyflake) toID() (int64, error) {
156-
if sf.elapsedTime >= 1<<BitLenTime {
206+
if sf.elapsedTime >= 1<<sf.bitsTime {
157207
return 0, ErrOverTimeLimit
158208
}
159209

160-
return sf.elapsedTime<<(BitLenSequence+BitLenMachine) |
161-
int64(sf.sequence)<<BitLenMachine |
210+
return sf.elapsedTime<<(sf.bitsSequence+sf.bitsMachine) |
211+
int64(sf.sequence)<<sf.bitsMachine |
162212
int64(sf.machine), nil
163213
}
164214

@@ -198,35 +248,32 @@ func lower16BitPrivateIP(interfaceAddrs types.InterfaceAddrs) (int, error) {
198248
}
199249

200250
func (sf *Sonyflake) ToTime(id int64) time.Time {
201-
return time.Unix(0, (sf.startTime+Time(id))*sf.timeUnit)
202-
}
203-
204-
// Time returns the Sonyflake time when the given ID was generated.
205-
func Time(id int64) int64 {
206-
return id >> (BitLenSequence + BitLenMachine)
207-
}
208-
209-
// SequenceNumber returns the sequence number of a Sonyflake ID.
210-
func SequenceNumber(id int64) int {
211-
const maskSequence = int64((1<<BitLenSequence - 1) << BitLenMachine)
212-
return int((id & maskSequence) >> BitLenMachine)
213-
}
214-
215-
// MachineID returns the machine ID of a Sonyflake ID.
216-
func MachineID(id int64) int {
217-
const maskMachine = int64(1<<BitLenMachine - 1)
218-
return int(id & maskMachine)
251+
return time.Unix(0, (sf.startTime+sf.timePart(id))*sf.timeUnit)
219252
}
220253

221254
// Decompose returns a set of Sonyflake ID parts.
222-
func Decompose(id int64) map[string]int64 {
223-
time := Time(id)
224-
sequence := SequenceNumber(id)
225-
machine := MachineID(id)
255+
func (sf *Sonyflake) Decompose(id int64) map[string]int64 {
256+
time := sf.timePart(id)
257+
sequence := sf.sequencePart(id)
258+
machine := sf.machinePart(id)
226259
return map[string]int64{
227260
"id": id,
228261
"time": time,
229-
"sequence": int64(sequence),
230-
"machine": int64(machine),
262+
"sequence": sequence,
263+
"machine": machine,
231264
}
232265
}
266+
267+
func (sf *Sonyflake) timePart(id int64) int64 {
268+
return id >> (sf.bitsSequence + sf.bitsMachine)
269+
}
270+
271+
func (sf *Sonyflake) sequencePart(id int64) int64 {
272+
maskSequence := int64((1<<sf.bitsSequence - 1) << sf.bitsMachine)
273+
return (id & maskSequence) >> sf.bitsMachine
274+
}
275+
276+
func (sf *Sonyflake) machinePart(id int64) int64 {
277+
maskMachine := int64(1<<sf.bitsMachine - 1)
278+
return id & maskMachine
279+
}

0 commit comments

Comments
 (0)