forked from Terry-Mao/gosnowflake
/
id.go
101 lines (90 loc) · 3.39 KB
/
id.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// Copyright © 2014 Terry Mao, LiuDing All rights reserved.
// This file is part of gosnowflake.
// gosnowflake is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// gosnowflake is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with gosnowflake. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"errors"
"fmt"
"github.com/golang/glog"
"sync"
"time"
)
const (
twepoch = int64(1288834974657)
workerIdBits = uint(5)
datacenterIdBits = uint(5)
maxWorkerId = -1 ^ (-1 << workerIdBits)
maxDatacenterId = -1 ^ (-1 << datacenterIdBits)
sequenceBits = uint(12)
workerIdShift = sequenceBits
datacenterIdShift = sequenceBits + workerIdBits
timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits
sequenceMask = -1 ^ (-1 << sequenceBits)
)
type IdWorker struct {
sequence int64
lastTimestamp int64
workerId int64
datacenterId int64
mutex *sync.Mutex
}
// NewIdWorker new a snowflake id generator object.
func NewIdWorker(workerId, datacenterId int64) (*IdWorker, error) {
idWorker := &IdWorker{}
if workerId > maxWorkerId || workerId < 0 {
glog.Errorf("worker Id can't be greater than %d or less than 0", maxWorkerId)
return nil, errors.New(fmt.Sprintf("worker Id: %d error", workerId))
}
if datacenterId > maxDatacenterId || datacenterId < 0 {
glog.Errorf("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)
return nil, errors.New(fmt.Sprintf("datacenter Id: %d error", datacenterId))
}
idWorker.workerId = workerId
idWorker.datacenterId = datacenterId
idWorker.lastTimestamp = -1
idWorker.sequence = 0
idWorker.mutex = &sync.Mutex{}
glog.V(1).Infof("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d", timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId)
return idWorker, nil
}
// timeGen generate a unix millisecond.
func timeGen() int64 {
return time.Now().UnixNano() / int64(time.Millisecond)
}
// tilNextMillis spin wait till next millisecond.
func tilNextMillis(lastTimestamp int64) int64 {
timestamp := timeGen()
for timestamp <= lastTimestamp {
timestamp = timeGen()
}
return timestamp
}
// NextId get a snowflake id.
func (id *IdWorker) NextId() (int64, error) {
id.mutex.Lock()
defer id.mutex.Unlock()
timestamp := timeGen()
if timestamp < id.lastTimestamp {
glog.Errorf("clock is moving backwards. Rejecting requests until %d.", id.lastTimestamp)
return 0, errors.New(fmt.Sprintf("Clock moved backwards. Refusing to generate id for %d milliseconds", id.lastTimestamp-timestamp))
}
if id.lastTimestamp == timestamp {
id.sequence = (id.sequence + 1) & sequenceMask
if id.sequence == 0 {
timestamp = tilNextMillis(id.lastTimestamp)
}
} else {
id.sequence = 0
}
id.lastTimestamp = timestamp
return ((timestamp - twepoch) << timestampLeftShift) | (id.datacenterId << datacenterIdShift) | (id.workerId << workerIdShift) | id.sequence, nil
}