Merge branch 'master' into monotonic_clock

This commit is contained in:
Nishaad Ajani 2019-04-10 17:14:22 +08:00 committed by GitHub
commit a3f3d0ff71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 80 additions and 20 deletions

View File

@ -1,8 +1,12 @@
language: go language: go
go: go:
- 1.4 - 1.11.x
- 1.5 - 1.12.x
- 1.6
install: install:
- go get github.com/bwmarrin/flake - go get -v .
- go get -v . - go get -v golang.org/x/lint/golint
script:
- diff <(gofmt -d .) <(echo -n)
- go vet -x ./...
- golint -set_exit_status ./...
- go test -v -race ./...

View File

@ -135,7 +135,7 @@ To benchmark the generator on your system run the following command inside the
snowflake package directory. snowflake package directory.
```sh ```sh
go test -bench=. go test -run=^$ -bench=.
``` ```
If your curious, check out this commit that shows benchmarks that compare a few If your curious, check out this commit that shows benchmarks that compare a few

View File

@ -18,19 +18,21 @@ var (
// Note: add time.Duration to time.Now() to make sure we use the monotonic clock if available. // Note: add time.Duration to time.Now() to make sure we use the monotonic clock if available.
Epoch = curTime.Add(time.Date(2010, time.November, 4, 1, 42, 54, 0, time.UTC).Sub(curTime)) Epoch = curTime.Add(time.Date(2010, time.November, 4, 1, 42, 54, 0, time.UTC).Sub(curTime))
// Number of bits to use for Node // NodeBits holds the number of bits to use for Node
// Remember, you have a total 22 bits to share between Node/Step // Remember, you have a total 22 bits to share between Node/Step
NodeBits uint8 = 10 NodeBits uint8 = 10
// Number of bits to use for Step // StepBits holds the number of bits to use for Step
// Remember, you have a total 22 bits to share between Node/Step // Remember, you have a total 22 bits to share between Node/Step
StepBits uint8 = 12 StepBits uint8 = 12
// DEPRECATED: the below four variables will be removed in a future release.
mu sync.Mutex
nodeMax int64 = -1 ^ (-1 << NodeBits) nodeMax int64 = -1 ^ (-1 << NodeBits)
nodeMask int64 = nodeMax << StepBits nodeMask = nodeMax << StepBits
stepMask int64 = -1 ^ (-1 << StepBits) stepMask int64 = -1 ^ (-1 << StepBits)
timeShift uint8 = NodeBits + StepBits timeShift = NodeBits + StepBits
nodeShift uint8 = StepBits nodeShift = StepBits
) )
const encodeBase32Map = "ybndrfg8ejkmcpqxot1uwisza345h769" const encodeBase32Map = "ybndrfg8ejkmcpqxot1uwisza345h769"
@ -81,6 +83,12 @@ type Node struct {
time time.Duration time time.Duration
node int64 node int64
step int64 step int64
nodeMax int64
nodeMask int64
stepMask int64
timeShift uint8
nodeShift uint8
} }
// An ID is a custom type used for a snowflake ID. This is used so we can // An ID is a custom type used for a snowflake ID. This is used so we can
@ -92,17 +100,28 @@ type ID int64
func NewNode(node int64) (*Node, error) { func NewNode(node int64) (*Node, error) {
// re-calc in case custom NodeBits or StepBits were set // re-calc in case custom NodeBits or StepBits were set
// DEPRECATED: the below block will be removed in a future release.
mu.Lock()
nodeMax = -1 ^ (-1 << NodeBits) nodeMax = -1 ^ (-1 << NodeBits)
nodeMask = nodeMax << StepBits nodeMask = nodeMax << StepBits
stepMask = -1 ^ (-1 << StepBits) stepMask = -1 ^ (-1 << StepBits)
timeShift = NodeBits + StepBits timeShift = NodeBits + StepBits
nodeShift = StepBits nodeShift = StepBits
mu.Unlock()
if node < 0 || node > nodeMax { n := Node{}
return nil, errors.New("Node number must be between 0 and " + strconv.FormatInt(nodeMax, 10)) n.node = node
n.nodeMax = -1 ^ (-1 << NodeBits)
n.nodeMask = n.nodeMax << StepBits
n.stepMask = -1 ^ (-1 << StepBits)
n.timeShift = NodeBits + StepBits
n.nodeShift = StepBits
if n.node < 0 || n.node > n.nodeMax {
return nil, errors.New("Node number must be between 0 and " + strconv.FormatInt(n.nodeMax, 10))
} }
return &Node{node: node}, nil return &n, nil
} }
// Generate creates and returns a unique snowflake ID // Generate creates and returns a unique snowflake ID
@ -113,7 +132,7 @@ func (n *Node) Generate() ID {
now := time.Since(Epoch) now := time.Since(Epoch)
if now-n.time < time.Millisecond { if now-n.time < time.Millisecond {
n.step = (n.step + 1) & stepMask n.step = (n.step + 1) & n.stepMask
if n.step == 0 { if n.step == 0 {
for now-n.time < time.Millisecond { for now-n.time < time.Millisecond {
@ -126,8 +145,8 @@ func (n *Node) Generate() ID {
n.time = now n.time = now
r := ID((now.Nanoseconds()/1000000)<<timeShift | r := ID((now.Nanoseconds()/1000000)<<n.timeShift |
(n.node << nodeShift) | (n.node << n.nodeShift) |
(n.step), (n.step),
) )
@ -251,16 +270,19 @@ func (f ID) IntBytes() [8]byte {
} }
// Time returns an int64 unix timestamp in miliseconds of the snowflake ID time // Time returns an int64 unix timestamp in miliseconds of the snowflake ID time
// DEPRECATED: the below function will be removed in a future release.
func (f ID) Time() int64 { func (f ID) Time() int64 {
return (int64(f) >> timeShift) + (Epoch.UnixNano() / 1000000) return (int64(f) >> timeShift) + (Epoch.UnixNano() / 1000000)
} }
// Node returns an int64 of the snowflake ID node number // Node returns an int64 of the snowflake ID node number
// DEPRECATED: the below function will be removed in a future release.
func (f ID) Node() int64 { func (f ID) Node() int64 {
return int64(f) & nodeMask >> nodeShift return int64(f) & nodeMask >> nodeShift
} }
// Step returns an int64 of the snowflake step (or sequence) number // Step returns an int64 of the snowflake step (or sequence) number
// DEPRECATED: the below function will be removed in a future release.
func (f ID) Step() int64 { func (f ID) Step() int64 {
return int64(f) & stepMask return int64(f) & stepMask
} }

View File

@ -6,6 +6,25 @@ import (
"testing" "testing"
) )
// I feel like there's probably a better way
func TestRace(t *testing.T) {
node, _ := NewNode(1)
go func() {
for i := 0; i < 1000000000; i++ {
NewNode(1)
}
}()
for i := 0; i < 4000; i++ {
node.Generate()
}
}
func TestMarshalJSON(t *testing.T) { func TestMarshalJSON(t *testing.T) {
id := ID(13587) id := ID(13587)
expected := "\"13587\"" expected := "\"13587\""
@ -31,7 +50,7 @@ func TestMarshalsIntBytes(t *testing.T) {
func TestUnmarshalJSON(t *testing.T) { func TestUnmarshalJSON(t *testing.T) {
tt := []struct { tt := []struct {
json string json string
expectedId ID expectedID ID
expectedErr error expectedErr error
}{ }{
{`"13587"`, 13587, nil}, {`"13587"`, 13587, nil},
@ -46,8 +65,8 @@ func TestUnmarshalJSON(t *testing.T) {
t.Errorf("Expected to get error '%s' decoding JSON, but got '%s'", tc.expectedErr, err) t.Errorf("Expected to get error '%s' decoding JSON, but got '%s'", tc.expectedErr, err)
} }
if id != tc.expectedId { if id != tc.expectedID {
t.Errorf("Expected to get ID '%s' decoding JSON, but got '%s'", tc.expectedId, id) t.Errorf("Expected to get ID '%s' decoding JSON, but got '%s'", tc.expectedID, id)
} }
} }
} }
@ -112,6 +131,7 @@ func TestBase58(t *testing.T) {
} }
} }
} }
func BenchmarkParseBase58(b *testing.B) { func BenchmarkParseBase58(b *testing.B) {
node, _ := NewNode(1) node, _ := NewNode(1)
@ -149,6 +169,20 @@ func BenchmarkGenerate(b *testing.B) {
} }
} }
func BenchmarkGenerateMaxSequence(b *testing.B) {
NodeBits = 1
StepBits = 21
node, _ := NewNode(1)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
_ = node.Generate()
}
}
func BenchmarkUnmarshal(b *testing.B) { func BenchmarkUnmarshal(b *testing.B) {
// Generate the ID to unmarshal // Generate the ID to unmarshal
node, _ := NewNode(1) node, _ := NewNode(1)