From 2b49b6909e3b5b1eea3322a791bb94d658ce7a72 Mon Sep 17 00:00:00 2001 From: FanOne Date: Thu, 29 Feb 2024 13:38:09 +0000 Subject: [PATCH 1/5] feat:snowflake --- .../process_ctrl_statemachine_engine.go | 3 ++- .../statemachine/engine/sequence/snowflake.go | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 pkg/saga/statemachine/engine/sequence/snowflake.go diff --git a/pkg/saga/statemachine/engine/process_ctrl_statemachine_engine.go b/pkg/saga/statemachine/engine/process_ctrl_statemachine_engine.go index afec63cbd..aae9fe89a 100644 --- a/pkg/saga/statemachine/engine/process_ctrl_statemachine_engine.go +++ b/pkg/saga/statemachine/engine/process_ctrl_statemachine_engine.go @@ -2,12 +2,13 @@ package engine import ( "context" + "time" + "github.com/pkg/errors" "github.com/seata/seata-go/pkg/saga/statemachine/constant" "github.com/seata/seata-go/pkg/saga/statemachine/engine/events" "github.com/seata/seata-go/pkg/saga/statemachine/engine/process_ctrl" "github.com/seata/seata-go/pkg/saga/statemachine/statelang" - "time" ) type ProcessCtrlStateMachineEngine struct { diff --git a/pkg/saga/statemachine/engine/sequence/snowflake.go b/pkg/saga/statemachine/engine/sequence/snowflake.go new file mode 100644 index 000000000..fc4c70255 --- /dev/null +++ b/pkg/saga/statemachine/engine/sequence/snowflake.go @@ -0,0 +1,24 @@ +package sequence + +import "sync" + +// SnowflakeSeqGenerator Snowflake gen ids +// ref: https://en.wikipedia.org/wiki/Snowflake_ID +type SnowflakeSeqGenerator struct { + Mutex *sync.Mutex + Timestamp int64 + DataCenterId int64 + WorkerId int64 + Sequence int64 +} + +func NewSnowflakeSeqSeqGenerator(dataCenterId, workId int64) *SnowflakeSeqGenerator { + return &SnowflakeSeqGenerator{ + DataCenterId: dataCenterId, + WorkerId: workId, + } +} + +func (U SnowflakeSeqGenerator) GenerateId() string { + return "" +} From 5978a43d6a2cddb7cec54a081b31bfac1d0a4007 Mon Sep 17 00:00:00 2001 From: FanOne Date: Fri, 1 Mar 2024 03:26:08 +0000 Subject: [PATCH 2/5] feat:snowflake gen ids --- .../statemachine/engine/sequence/snowflake.go | 80 ++++++++++++++++--- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/pkg/saga/statemachine/engine/sequence/snowflake.go b/pkg/saga/statemachine/engine/sequence/snowflake.go index fc4c70255..1a80ac6df 100644 --- a/pkg/saga/statemachine/engine/sequence/snowflake.go +++ b/pkg/saga/statemachine/engine/sequence/snowflake.go @@ -1,24 +1,82 @@ package sequence -import "sync" +import ( + "fmt" + "sync" + "time" +) // SnowflakeSeqGenerator Snowflake gen ids // ref: https://en.wikipedia.org/wiki/Snowflake_ID + +var ( + epoch = time.Date(2010, time.November, 01, 42, 54, 00, 00, time.UTC).UnixMicro() +) + +const ( + timestampBits = 41 + dataCenterIdBits = 5 + workerIdBits = 5 + SeqBits = 12 + + defaultInitValue = 0 + timestampMaxValue = -1 ^ (-1 << timestampBits) + dataCenterIdMaxValue = -1 ^ (-1 << dataCenterIdBits) + workerIdBitsMaxValue = -1 ^ (-1 << workerIdBits) + seqBitsMaxValue = -1 ^ (-1 << SeqBits) + + workIdShift = 12 + dataCenterIdShift = 17 + timestampShift = 22 +) + type SnowflakeSeqGenerator struct { - Mutex *sync.Mutex - Timestamp int64 - DataCenterId int64 - WorkerId int64 - Sequence int64 + mu *sync.Mutex + timestamp int64 + dataCenterId int64 + workerId int64 + sequence int64 } -func NewSnowflakeSeqSeqGenerator(dataCenterId, workId int64) *SnowflakeSeqGenerator { - return &SnowflakeSeqGenerator{ - DataCenterId: dataCenterId, - WorkerId: workId, +func NewSnowflakeSeqSeqGenerator(dataCenterId, workId int64) (r *SnowflakeSeqGenerator, err error) { + if dataCenterId < 0 || dataCenterId > dataCenterIdMaxValue { + err = fmt.Errorf("dataCenterId must be between 0 and %d", dataCenterIdMaxValue-1) + return } + if workId < 0 || workId > workerIdBitsMaxValue { + err = fmt.Errorf("workId must be between 0 and %d", dataCenterIdMaxValue-1) + return + } + return &SnowflakeSeqGenerator{ + timestamp: defaultInitValue, + dataCenterId: dataCenterId, + workerId: workId, + sequence: defaultInitValue, + }, nil } func (U SnowflakeSeqGenerator) GenerateId() string { - return "" + U.mu.Lock() + defer U.mu.Unlock() + + now := time.Now().UnixMilli() + if U.timestamp == now { + U.sequence = (U.sequence + 1) & seqBitsMaxValue + if U.sequence == 0 { + for now <= U.timestamp { + now = time.Now().UnixMilli() + } + } + } else { + U.sequence = defaultInitValue + } + tmp := now - epoch + if tmp > timestampMaxValue { + // logger + return "" + } + U.timestamp = now + r := (tmp)< Date: Fri, 1 Mar 2024 08:11:38 +0000 Subject: [PATCH 3/5] feat:word spelling --- .../statemachine/engine/sequence/snowflake.go | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/pkg/saga/statemachine/engine/sequence/snowflake.go b/pkg/saga/statemachine/engine/sequence/snowflake.go index 1a80ac6df..dbc87991d 100644 --- a/pkg/saga/statemachine/engine/sequence/snowflake.go +++ b/pkg/saga/statemachine/engine/sequence/snowflake.go @@ -10,7 +10,7 @@ import ( // ref: https://en.wikipedia.org/wiki/Snowflake_ID var ( - epoch = time.Date(2010, time.November, 01, 42, 54, 00, 00, time.UTC).UnixMicro() + epoch = time.Date(2010, time.November, 01, 42, 54, 00, 00, time.UTC).UnixMilli() ) const ( @@ -19,15 +19,16 @@ const ( workerIdBits = 5 SeqBits = 12 - defaultInitValue = 0 timestampMaxValue = -1 ^ (-1 << timestampBits) dataCenterIdMaxValue = -1 ^ (-1 << dataCenterIdBits) - workerIdBitsMaxValue = -1 ^ (-1 << workerIdBits) + workerIdMaxValue = -1 ^ (-1 << workerIdBits) seqBitsMaxValue = -1 ^ (-1 << SeqBits) workIdShift = 12 dataCenterIdShift = 17 timestampShift = 22 + + defaultInitValue = 0 ) type SnowflakeSeqGenerator struct { @@ -43,7 +44,7 @@ func NewSnowflakeSeqSeqGenerator(dataCenterId, workId int64) (r *SnowflakeSeqGen err = fmt.Errorf("dataCenterId must be between 0 and %d", dataCenterIdMaxValue-1) return } - if workId < 0 || workId > workerIdBitsMaxValue { + if workId < 0 || workId > workerIdMaxValue { err = fmt.Errorf("workId must be between 0 and %d", dataCenterIdMaxValue-1) return } @@ -55,28 +56,28 @@ func NewSnowflakeSeqSeqGenerator(dataCenterId, workId int64) (r *SnowflakeSeqGen }, nil } -func (U SnowflakeSeqGenerator) GenerateId() string { - U.mu.Lock() - defer U.mu.Unlock() +func (S SnowflakeSeqGenerator) GenerateId() string { + S.mu.Lock() + defer S.mu.Unlock() now := time.Now().UnixMilli() - if U.timestamp == now { - U.sequence = (U.sequence + 1) & seqBitsMaxValue - if U.sequence == 0 { - for now <= U.timestamp { + if S.timestamp == now { + S.sequence = (S.sequence + 1) & seqBitsMaxValue + if S.sequence == 0 { + for now <= S.timestamp { now = time.Now().UnixMilli() } } } else { - U.sequence = defaultInitValue + S.sequence = defaultInitValue } tmp := now - epoch if tmp > timestampMaxValue { // logger return "" } - U.timestamp = now - r := (tmp)< Date: Sat, 2 Mar 2024 00:19:52 +0800 Subject: [PATCH 4/5] feat:snowflake gen ids --- .../statemachine/engine/sequence/snowflake.go | 73 ++++++++++++++----- .../engine/sequence/snowflake_test.go | 26 +++++++ 2 files changed, 79 insertions(+), 20 deletions(-) create mode 100644 pkg/saga/statemachine/engine/sequence/snowflake_test.go diff --git a/pkg/saga/statemachine/engine/sequence/snowflake.go b/pkg/saga/statemachine/engine/sequence/snowflake.go index dbc87991d..192525948 100644 --- a/pkg/saga/statemachine/engine/sequence/snowflake.go +++ b/pkg/saga/statemachine/engine/sequence/snowflake.go @@ -4,29 +4,43 @@ import ( "fmt" "sync" "time" + + "github.com/seata/seata-go/pkg/util/log" ) // SnowflakeSeqGenerator Snowflake gen ids // ref: https://en.wikipedia.org/wiki/Snowflake_ID var ( - epoch = time.Date(2010, time.November, 01, 42, 54, 00, 00, time.UTC).UnixMilli() + // set the beginning time + epoch = time.Date(2024, time.January, 01, 00, 00, 00, 00, time.UTC) ) const ( - timestampBits = 41 + // timestamp occupancy bits + timestampBits = 41 + // dataCenterId occupancy bits dataCenterIdBits = 5 - workerIdBits = 5 - SeqBits = 12 + // workerId occupancy bits + workerIdBits = 5 + // sequence occupancy bits + seqBits = 12 - timestampMaxValue = -1 ^ (-1 << timestampBits) + // timestamp max value, just like 2^41-1 = 2199023255551 + timestampMaxValue = -1 ^ (-1 << timestampBits) + // dataCenterId max value, just like 2^5-1 = 31 dataCenterIdMaxValue = -1 ^ (-1 << dataCenterIdBits) - workerIdMaxValue = -1 ^ (-1 << workerIdBits) - seqBitsMaxValue = -1 ^ (-1 << SeqBits) + // workId max value, just like 2^5-1 = 31 + workerIdMaxValue = -1 ^ (-1 << workerIdBits) + // sequence max value, just like 2^12-1 = 4095 + seqMaxValue = -1 ^ (-1 << seqBits) - workIdShift = 12 + // number of workId offsets (seqBits) + workIdShift = 12 + // number of dataCenterId offsets (seqBits + workerIdBits) dataCenterIdShift = 17 - timestampShift = 22 + // number of timestamp offsets (seqBits + workerIdBits + dataCenterIdBits) + timestampShift = 22 defaultInitValue = 0 ) @@ -39,45 +53,64 @@ type SnowflakeSeqGenerator struct { sequence int64 } -func NewSnowflakeSeqSeqGenerator(dataCenterId, workId int64) (r *SnowflakeSeqGenerator, err error) { +// NewSnowflakeSeqGenerator initiates the snowflake generator +func NewSnowflakeSeqGenerator(dataCenterId, workId int64) (r *SnowflakeSeqGenerator, err error) { if dataCenterId < 0 || dataCenterId > dataCenterIdMaxValue { - err = fmt.Errorf("dataCenterId must be between 0 and %d", dataCenterIdMaxValue-1) + err = fmt.Errorf("dataCenterId should between 0 and %d", dataCenterIdMaxValue-1) return } + if workId < 0 || workId > workerIdMaxValue { - err = fmt.Errorf("workId must be between 0 and %d", dataCenterIdMaxValue-1) + err = fmt.Errorf("workId should between 0 and %d", dataCenterIdMaxValue-1) return } + return &SnowflakeSeqGenerator{ - timestamp: defaultInitValue, + mu: new(sync.Mutex), + timestamp: defaultInitValue - 1, dataCenterId: dataCenterId, workerId: workId, sequence: defaultInitValue, }, nil } -func (S SnowflakeSeqGenerator) GenerateId() string { +// GenerateId timestamp + dataCenterId + workId + sequence +func (S *SnowflakeSeqGenerator) GenerateId(entity string, ruleName string) string { S.mu.Lock() defer S.mu.Unlock() - now := time.Now().UnixMilli() + now := time.Since(epoch).Milliseconds() + + if S.timestamp > now { // Clock callback + log.Errorf("Clock moved backwards. Refusing to generate ID, last timestamp is %d, now is %d", S.timestamp, now) + return "" + } + if S.timestamp == now { - S.sequence = (S.sequence + 1) & seqBitsMaxValue + // generate multiple IDs in the same millisecond, incrementing the sequence number to prevent conflicts + S.sequence = (S.sequence + 1) & seqMaxValue if S.sequence == 0 { + // sequence overflow, waiting for next millisecond for now <= S.timestamp { - now = time.Now().UnixMilli() + now = time.Since(epoch).Milliseconds() } } } else { + // initialized sequences are used directly at different millisecond timestamps S.sequence = defaultInitValue } - tmp := now - epoch + tmp := now - epoch.UnixMilli() if tmp > timestampMaxValue { - // logger + log.Errorf("epoch should between 0 and %d", timestampMaxValue-1) return "" } S.timestamp = now - r := (tmp)< Date: Sat, 2 Mar 2024 00:23:58 +0800 Subject: [PATCH 5/5] feat: add code comments --- pkg/saga/statemachine/engine/sequence/snowflake.go | 12 ++++++------ .../statemachine/engine/sequence/snowflake_test.go | 4 +++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pkg/saga/statemachine/engine/sequence/snowflake.go b/pkg/saga/statemachine/engine/sequence/snowflake.go index 192525948..f5622b064 100644 --- a/pkg/saga/statemachine/engine/sequence/snowflake.go +++ b/pkg/saga/statemachine/engine/sequence/snowflake.go @@ -8,12 +8,12 @@ import ( "github.com/seata/seata-go/pkg/util/log" ) -// SnowflakeSeqGenerator Snowflake gen ids +// SnowflakeSeqGenerator snowflake gen ids // ref: https://en.wikipedia.org/wiki/Snowflake_ID var ( // set the beginning time - epoch = time.Date(2024, time.January, 01, 00, 00, 00, 00, time.UTC) + epoch = time.Date(2024, time.January, 01, 00, 00, 00, 00, time.UTC).UnixMilli() ) const ( @@ -79,7 +79,7 @@ func (S *SnowflakeSeqGenerator) GenerateId(entity string, ruleName string) strin S.mu.Lock() defer S.mu.Unlock() - now := time.Since(epoch).Milliseconds() + now := time.Now().UnixMilli() if S.timestamp > now { // Clock callback log.Errorf("Clock moved backwards. Refusing to generate ID, last timestamp is %d, now is %d", S.timestamp, now) @@ -92,14 +92,14 @@ func (S *SnowflakeSeqGenerator) GenerateId(entity string, ruleName string) strin if S.sequence == 0 { // sequence overflow, waiting for next millisecond for now <= S.timestamp { - now = time.Since(epoch).Milliseconds() + now = time.Now().UnixMilli() } } } else { // initialized sequences are used directly at different millisecond timestamps S.sequence = defaultInitValue } - tmp := now - epoch.UnixMilli() + tmp := now - epoch if tmp > timestampMaxValue { log.Errorf("epoch should between 0 and %d", timestampMaxValue-1) return "" @@ -107,7 +107,7 @@ func (S *SnowflakeSeqGenerator) GenerateId(entity string, ruleName string) strin S.timestamp = now // combine the parts to generate the final ID and convert the 64-bit binary to decimal digits. - r := (now)<