Browse Source

feature saga :support generate id by Snowflake (#670)

pull/681/head
FanOne GitHub 1 year ago
parent
commit
d1fad744aa
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
3 changed files with 146 additions and 1 deletions
  1. +2
    -1
      pkg/saga/statemachine/engine/process_ctrl_statemachine_engine.go
  2. +116
    -0
      pkg/saga/statemachine/engine/sequence/snowflake.go
  3. +28
    -0
      pkg/saga/statemachine/engine/sequence/snowflake_test.go

+ 2
- 1
pkg/saga/statemachine/engine/process_ctrl_statemachine_engine.go View File

@@ -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 {


+ 116
- 0
pkg/saga/statemachine/engine/sequence/snowflake.go View File

@@ -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)
}

+ 28
- 0
pkg/saga/statemachine/engine/sequence/snowflake_test.go View File

@@ -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)))
}

Loading…
Cancel
Save