This is an automated email from the ASF dual-hosted git repository.

yixia pushed a commit to branch feature/saga
in repository https://gitbox.apache.org/repos/asf/incubator-seata-go.git


The following commit(s) were added to refs/heads/feature/saga by this push:
     new d1fad744 feature saga :support generate id by Snowflake (#670)
d1fad744 is described below

commit d1fad744aa7b6acc01efcc87a0a1315bec72a18d
Author: FanOne <[email protected]>
AuthorDate: Sat Mar 16 17:46:49 2024 +0800

    feature saga :support generate id by Snowflake (#670)
---
 .../engine/process_ctrl_statemachine_engine.go     |   3 +-
 pkg/saga/statemachine/engine/sequence/snowflake.go | 116 +++++++++++++++++++++
 .../statemachine/engine/sequence/snowflake_test.go |  28 +++++
 3 files changed, 146 insertions(+), 1 deletion(-)

diff --git a/pkg/saga/statemachine/engine/process_ctrl_statemachine_engine.go 
b/pkg/saga/statemachine/engine/process_ctrl_statemachine_engine.go
index afec63cb..aae9fe89 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 00000000..f5622b06
--- /dev/null
+++ b/pkg/saga/statemachine/engine/sequence/snowflake.go
@@ -0,0 +1,116 @@
+package sequence
+
+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 (
+       // set the beginning time
+       epoch = time.Date(2024, time.January, 01, 00, 00, 00, 00, 
time.UTC).UnixMilli()
+)
+
+const (
+       // timestamp occupancy bits
+       timestampBits = 41
+       // dataCenterId occupancy bits
+       dataCenterIdBits = 5
+       // workerId occupancy bits
+       workerIdBits = 5
+       // sequence occupancy bits
+       seqBits = 12
+
+       // 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)
+       // 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)
+
+       // number of workId offsets (seqBits)
+       workIdShift = 12
+       // number of dataCenterId offsets (seqBits + workerIdBits)
+       dataCenterIdShift = 17
+       // number of timestamp offsets (seqBits + workerIdBits + 
dataCenterIdBits)
+       timestampShift = 22
+
+       defaultInitValue = 0
+)
+
+type SnowflakeSeqGenerator struct {
+       mu           *sync.Mutex
+       timestamp    int64
+       dataCenterId int64
+       workerId     int64
+       sequence     int64
+}
+
+// NewSnowflakeSeqGenerator initiates the snowflake generator
+func NewSnowflakeSeqGenerator(dataCenterId, workId int64) (r 
*SnowflakeSeqGenerator, err error) {
+       if dataCenterId < 0 || dataCenterId > dataCenterIdMaxValue {
+               err = fmt.Errorf("dataCenterId should between 0 and %d", 
dataCenterIdMaxValue-1)
+               return
+       }
+
+       if workId < 0 || workId > workerIdMaxValue {
+               err = fmt.Errorf("workId should between 0 and %d", 
dataCenterIdMaxValue-1)
+               return
+       }
+
+       return &SnowflakeSeqGenerator{
+               mu:           new(sync.Mutex),
+               timestamp:    defaultInitValue - 1,
+               dataCenterId: dataCenterId,
+               workerId:     workId,
+               sequence:     defaultInitValue,
+       }, nil
+}
+
+// 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()
+
+       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 {
+               // 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()
+                       }
+               }
+       } else {
+               // initialized sequences are used directly at different 
millisecond timestamps
+               S.sequence = defaultInitValue
+       }
+       tmp := now - epoch
+       if tmp > timestampMaxValue {
+               log.Errorf("epoch should between 0 and %d", timestampMaxValue-1)
+               return ""
+       }
+       S.timestamp = now
+
+       // combine the parts to generate the final ID and convert the 64-bit 
binary to decimal digits.
+       r := (tmp)<<timestampShift |
+               (S.dataCenterId << dataCenterIdShift) |
+               (S.workerId << workIdShift) |
+               (S.sequence)
+
+       return fmt.Sprintf("%d", r)
+}
diff --git a/pkg/saga/statemachine/engine/sequence/snowflake_test.go 
b/pkg/saga/statemachine/engine/sequence/snowflake_test.go
new file mode 100644
index 00000000..451f478d
--- /dev/null
+++ b/pkg/saga/statemachine/engine/sequence/snowflake_test.go
@@ -0,0 +1,28 @@
+package sequence
+
+import (
+       "strconv"
+       "testing"
+)
+
+func TestSnowflakeSeqGenerator_GenerateId(t *testing.T) {
+       var dataCenterId, workId int64 = 1, 1
+       generator, err := NewSnowflakeSeqGenerator(dataCenterId, workId)
+       if err != nil {
+               t.Error(err)
+               return
+       }
+       var x, y string
+       for i := 0; i < 100; i++ {
+               y = generator.GenerateId("", "")
+               if x == y {
+                       t.Errorf("x(%s) & y(%s) are the same", x, y)
+               }
+               x = y
+       }
+}
+
+func TestEpoch(t *testing.T) {
+       t.Log(epoch)
+       t.Log(len(strconv.FormatInt(epoch, 10)))
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to