From f5be30148bbc327629e69a43a60e3ddc7a14cc14 Mon Sep 17 00:00:00 2001 From: ccfuncy Date: Sat, 6 May 2023 15:47:06 +0800 Subject: [PATCH] fix clock backward bug --- README.md | 6 ++++++ sonyflake.go | 41 ++++++++++++++++++++++++++++++++++++----- sonyflake_test.go | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9c9ebeb..39ab7ed 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/sonyflake.go b/sonyflake.go index 1c1402e..d4394fe 100644 --- a/sonyflake.go +++ b/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< 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)) +}