mirror of
https://github.com/sony/sonyflake.git
synced 2026-01-06 19:26:19 +00:00
fix clock backward bug
This commit is contained in:
@@ -71,6 +71,12 @@ func (sf *Sonyflake) NextID() (uint64, error)
|
||||
NextID can continue to generate IDs for about 174 years from StartTime.
|
||||
But after the Sonyflake time is over the limit, NextID returns an error.
|
||||
|
||||
if you want to use the mono time, you can call the method NextIDMono.
|
||||
|
||||
```go
|
||||
func (sf *Sonyflake) NextIDMono() (uint64, error)
|
||||
```
|
||||
NextIDMono can avoid the clock backwards.
|
||||
> **Note:**
|
||||
> Sonyflake currently does not use the most significant bit of IDs,
|
||||
> so you can convert Sonyflake IDs from `uint64` to `int64` safely.
|
||||
|
||||
41
sonyflake.go
41
sonyflake.go
@@ -45,11 +45,12 @@ type Settings struct {
|
||||
|
||||
// Sonyflake is a distributed unique ID generator.
|
||||
type Sonyflake struct {
|
||||
mutex *sync.Mutex
|
||||
startTime int64
|
||||
elapsedTime int64
|
||||
sequence uint16
|
||||
machineID uint16
|
||||
mutex *sync.Mutex
|
||||
startTime int64
|
||||
startTimeMono time.Time
|
||||
elapsedTime int64
|
||||
sequence uint16
|
||||
machineID uint16
|
||||
}
|
||||
|
||||
var defaultInterfaceAddrs = net.InterfaceAddrs
|
||||
@@ -69,8 +70,10 @@ func NewSonyflake(st Settings) *Sonyflake {
|
||||
}
|
||||
if st.StartTime.IsZero() {
|
||||
sf.startTime = toSonyflakeTime(time.Date(2014, 9, 1, 0, 0, 0, 0, time.UTC))
|
||||
sf.startTimeMono = time.Now()
|
||||
} else {
|
||||
sf.startTime = toSonyflakeTime(st.StartTime)
|
||||
sf.startTimeMono = st.StartTime
|
||||
}
|
||||
|
||||
var err error
|
||||
@@ -110,6 +113,31 @@ func (sf *Sonyflake) NextID() (uint64, error) {
|
||||
return sf.toID()
|
||||
}
|
||||
|
||||
// NextIDMono generates a next unique ID.
|
||||
// After the Sonyflake time overflows, NextID returns an error.
|
||||
// This function use mono time to avoid clock backwards
|
||||
func (sf *Sonyflake) NextIDMono() (uint64, error) {
|
||||
const maskSequence = uint16(1<<BitLenSequence - 1)
|
||||
|
||||
sf.mutex.Lock()
|
||||
defer sf.mutex.Unlock()
|
||||
|
||||
current := currentElapsedTimeMono(sf.startTimeMono)
|
||||
if sf.elapsedTime < current {
|
||||
sf.elapsedTime = current
|
||||
sf.sequence = 0
|
||||
} else {
|
||||
sf.sequence = (sf.sequence + 1) & maskSequence
|
||||
if sf.sequence == 0 {
|
||||
sf.elapsedTime++
|
||||
overtime := sf.elapsedTime - current
|
||||
time.Sleep(sleepTime((overtime)))
|
||||
}
|
||||
}
|
||||
|
||||
return sf.toID()
|
||||
}
|
||||
|
||||
const sonyflakeTimeUnit = 1e7 // nsec, i.e. 10 msec
|
||||
|
||||
func toSonyflakeTime(t time.Time) int64 {
|
||||
@@ -119,6 +147,9 @@ func toSonyflakeTime(t time.Time) int64 {
|
||||
func currentElapsedTime(startTime int64) int64 {
|
||||
return toSonyflakeTime(time.Now()) - startTime
|
||||
}
|
||||
func currentElapsedTimeMono(startTimeMono time.Time) int64 {
|
||||
return time.Now().Sub(startTimeMono).Nanoseconds() / sonyflakeTimeUnit
|
||||
}
|
||||
|
||||
func sleepTime(overtime int64) time.Duration {
|
||||
return time.Duration(overtime*sonyflakeTimeUnit) -
|
||||
|
||||
@@ -282,3 +282,35 @@ func TestSonyflakeTimeUnit(t *testing.T) {
|
||||
t.Errorf("unexpected time unit")
|
||||
}
|
||||
}
|
||||
|
||||
func nextIDMono(t *testing.T) uint64 {
|
||||
id, err := sf.NextIDMono()
|
||||
if err != nil {
|
||||
t.Fatal("id not generated")
|
||||
}
|
||||
return id
|
||||
}
|
||||
func TestSonyflake_NextIDMono(t *testing.T) {
|
||||
sleepTime := time.Duration(50 * sonyflakeTimeUnit)
|
||||
time.Sleep(sleepTime)
|
||||
|
||||
id := nextIDMono(t)
|
||||
|
||||
actualTime := ElapsedTime(id)
|
||||
if actualTime < sleepTime || actualTime > sleepTime+sonyflakeTimeUnit {
|
||||
t.Errorf("unexpected time: %d", actualTime)
|
||||
}
|
||||
|
||||
actualSequence := SequenceNumber(id)
|
||||
if actualSequence != 0 {
|
||||
t.Errorf("unexpected sequence: %d", actualSequence)
|
||||
}
|
||||
|
||||
actualMachineID := MachineID(id)
|
||||
if actualMachineID != machineID {
|
||||
t.Errorf("unexpected machine id: %d", actualMachineID)
|
||||
}
|
||||
|
||||
fmt.Println("sonyflake id:", id)
|
||||
fmt.Println("decompose:", Decompose(id))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user