diff --git a/.licenserc.yaml b/.licenserc.yaml index c6e68ae7..59a3ee69 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -82,6 +82,7 @@ header: # `header` section is configurations for source codes license header. - 'pkg/resolver/mysql/encoding.go' - 'pkg/resolver/mysql/sql_error.go' - 'pkg/resolver/mysql/type.go' + - 'pkg/runtime/mock_runtime.go' - 'VERSION' - ".errcheck-exclude" - ".golangci.yml" diff --git a/pkg/runtime/mock_runtime.go b/pkg/runtime/mock_runtime.go new file mode 100644 index 00000000..aa87ea3c --- /dev/null +++ b/pkg/runtime/mock_runtime.go @@ -0,0 +1,132 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/arana-db/arana/pkg/runtime (interfaces: Runtime) + +// Package runtime is a generated GoMock package. +package runtime + +import ( + context "context" + reflect "reflect" +) + +import ( + gomock "github.com/golang/mock/gomock" +) + +import ( + proto "github.com/arana-db/arana/pkg/proto" + namespace "github.com/arana-db/arana/pkg/runtime/namespace" +) + +// MockRuntime is a mock of Runtime interface. +type MockRuntime struct { + ctrl *gomock.Controller + recorder *MockRuntimeMockRecorder +} + +// MockRuntimeMockRecorder is the mock recorder for MockRuntime. +type MockRuntimeMockRecorder struct { + mock *MockRuntime +} + +// NewMockRuntime creates a new mock instance. +func NewMockRuntime(ctrl *gomock.Controller) *MockRuntime { + mock := &MockRuntime{ctrl: ctrl} + mock.recorder = &MockRuntimeMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRuntime) EXPECT() *MockRuntimeMockRecorder { + return m.recorder +} + +// Begin mocks base method. +func (m *MockRuntime) Begin(arg0 context.Context, arg1 ...TxHook) (proto.Tx, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0} + for _, a := range arg1 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Begin", varargs...) + ret0, _ := ret[0].(proto.Tx) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Begin indicates an expected call of Begin. +func (mr *MockRuntimeMockRecorder) Begin(arg0 interface{}, arg1 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0}, arg1...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Begin", reflect.TypeOf((*MockRuntime)(nil).Begin), varargs...) +} + +// Exec mocks base method. +func (m *MockRuntime) Exec(arg0 context.Context, arg1, arg2 string, arg3 ...proto.Value) (proto.Result, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1, arg2} + for _, a := range arg3 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Exec", varargs...) + ret0, _ := ret[0].(proto.Result) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Exec indicates an expected call of Exec. +func (mr *MockRuntimeMockRecorder) Exec(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockRuntime)(nil).Exec), varargs...) +} + +// Execute mocks base method. +func (m *MockRuntime) Execute(arg0 *proto.Context) (proto.Result, uint16, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Execute", arg0) + ret0, _ := ret[0].(proto.Result) + ret1, _ := ret[1].(uint16) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// Execute indicates an expected call of Execute. +func (mr *MockRuntimeMockRecorder) Execute(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockRuntime)(nil).Execute), arg0) +} + +// Namespace mocks base method. +func (m *MockRuntime) Namespace() *namespace.Namespace { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Namespace") + ret0, _ := ret[0].(*namespace.Namespace) + return ret0 +} + +// Namespace indicates an expected call of Namespace. +func (mr *MockRuntimeMockRecorder) Namespace() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Namespace", reflect.TypeOf((*MockRuntime)(nil).Namespace)) +} + +// Query mocks base method. +func (m *MockRuntime) Query(arg0 context.Context, arg1, arg2 string, arg3 ...proto.Value) (proto.Result, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1, arg2} + for _, a := range arg3 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Query", varargs...) + ret0, _ := ret[0].(proto.Result) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Query indicates an expected call of Query. +func (mr *MockRuntimeMockRecorder) Query(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockRuntime)(nil).Query), varargs...) +} diff --git a/pkg/runtime/optimize/dml/select.go b/pkg/runtime/optimize/dml/select.go index f75193e7..c38bdca9 100644 --- a/pkg/runtime/optimize/dml/select.go +++ b/pkg/runtime/optimize/dml/select.go @@ -148,7 +148,7 @@ func optimizeSelect(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err if flag&_bypass != 0 { if len(stmt.From) > 0 { - err := rewriteSelectStatement(ctx, stmt, o) + err := expandSelectStar(ctx, stmt, o) if err != nil { return nil, err } @@ -197,7 +197,7 @@ func optimizeSelect(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err } toSingle := func(db, tbl string) (proto.Plan, error) { - if err := rewriteSelectStatement(ctx, stmt, o); err != nil { + if err := expandSelectStar(ctx, stmt, o); err != nil { return nil, err } ret := &dml.SimpleQueryPlan{ @@ -243,7 +243,7 @@ func optimizeSelect(ctx context.Context, o *optimize.Optimizer) (proto.Plan, err return toSingle(db, tbl) } - if err = rewriteSelectStatement(ctx, stmt, o); err != nil { + if err = expandSelectStar(ctx, stmt, o); err != nil { return nil, errors.WithStack(err) } @@ -570,7 +570,7 @@ func optimizeJoin(ctx context.Context, o *optimize.Optimizer, stmt *ast.SelectSt Stmt: selectStmt, } if _, ok = selectStmt.Select[0].(*ast.SelectElementAll); ok && len(selectStmt.Select) == 1 { - if err = rewriteSelectStatement(ctx, selectStmt, optimizer); err != nil { + if err = expandSelectStar(ctx, selectStmt, optimizer); err != nil { return nil, err } @@ -624,7 +624,7 @@ func optimizeJoin(ctx context.Context, o *optimize.Optimizer, stmt *ast.SelectSt scanner = newSelectScanner(stmt, o.Args) ) - if err = rewriteSelectStatement(ctx, stmt, o); err != nil { + if err = expandSelectStar(ctx, stmt, o); err != nil { return nil, errors.WithStack(err) } @@ -786,7 +786,7 @@ func overwriteLimit(stmt *ast.SelectStatement, args *[]proto.Value) (originOffse return } -func rewriteSelectStatement(ctx context.Context, stmt *ast.SelectStatement, o *optimize.Optimizer) error { +func expandSelectStar(ctx context.Context, stmt *ast.SelectStatement, o *optimize.Optimizer) error { // todo db 计算逻辑&tb shard 的计算逻辑 starExpand := false if len(stmt.Select) == 1 { diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go index 4af3cb40..e57b5e3d 100644 --- a/pkg/runtime/runtime.go +++ b/pkg/runtime/runtime.go @@ -15,6 +15,7 @@ * limitations under the License. */ +//go:generate mockgen -destination=./mock_runtime.go -package=runtime . Runtime package runtime import ( @@ -61,6 +62,8 @@ import ( var ( _ Runtime = (*defaultRuntime)(nil) _ proto.VConn = (*defaultRuntime)(nil) + + _runtimes sync.Map ) var Tracer = otel.Tracer("Runtime") @@ -77,8 +80,20 @@ type Runtime interface { Begin(ctx context.Context, hooks ...TxHook) (proto.Tx, error) } +// Register registers a Runtime. +func Register(tenant string, schema string, rt Runtime) error { + if _, loaded := _runtimes.LoadOrStore(tenant+":"+schema, rt); loaded { + return perrors.Errorf("cannot register conflict runtime: tenant=%s, name=%s", tenant, schema) + } + return nil +} + // Load loads a Runtime, here schema means logical database name. func Load(tenant, schema string) (Runtime, error) { + if rt, ok := _runtimes.Load(tenant + ":" + schema); ok { + return rt.(Runtime), nil + } + var ns *namespace.Namespace if ns = namespace.Load(tenant, schema); ns == nil { return nil, perrors.Errorf("no such schema: tenant=%s, schema=%s", tenant, schema) @@ -96,6 +111,7 @@ func Unload(tenant, schema string) error { var _ proto.DB = (*AtomDB)(nil) +// AtomDB represents an atom physical database instance type AtomDB struct { mu sync.Mutex diff --git a/pkg/sequence/group/group.go b/pkg/sequence/group/group.go index a1feb3a8..a5b1b0f1 100644 --- a/pkg/sequence/group/group.go +++ b/pkg/sequence/group/group.go @@ -77,14 +77,12 @@ type groupSequence struct { tableName string step int64 - nextGroupStartVal int64 - nextGroupMaxVal int64 currentGroupMaxVal int64 currentVal int64 } // Start sequence and do some initialization operations -func (seq *groupSequence) Start(ctx context.Context, option proto.SequenceConfig) error { +func (seq *groupSequence) Start(ctx context.Context, conf proto.SequenceConfig) error { rt := ctx.Value(proto.RuntimeCtxKey{}).(runtime.Runtime) ctx = rcontext.WithRead(rcontext.WithDirect(ctx)) @@ -94,11 +92,11 @@ func (seq *groupSequence) Start(ctx context.Context, option proto.SequenceConfig } // init sequence - if err := seq.initStep(option); err != nil { + if err := seq.initStep(conf); err != nil { return err } - seq.tableName = option.Name + seq.tableName = conf.Name return nil } @@ -131,12 +129,12 @@ func (seq *groupSequence) initTable(ctx context.Context, rt runtime.Runtime) err return nil } -func (seq *groupSequence) initStep(option proto.SequenceConfig) error { +func (seq *groupSequence) initStep(conf proto.SequenceConfig) error { seq.mu.Lock() defer seq.mu.Unlock() var step int64 - stepValue, ok := option.Option[_stepKey] + stepValue, ok := conf.Option[_stepKey] if ok { tempStep, err := strconv.Atoi(stepValue) if err != nil { @@ -169,8 +167,6 @@ func (seq *groupSequence) Acquire(ctx context.Context) (int64, error) { if err != nil { return 0, err } - seq.currentVal = seq.nextGroupStartVal - seq.currentGroupMaxVal = seq.nextGroupMaxVal } else { seq.currentVal++ } @@ -196,13 +192,14 @@ func (seq *groupSequence) acquireNextGroup(ctx context.Context, rt runtime.Runti if err != nil { return err } - val := make([]proto.Value, 1) + vals := make([]proto.Value, 1) row, err := ds.Next() if err != nil { + // first time, init the start seq val for the table if errors.Is(err, io.EOF) { - seq.nextGroupStartVal = _startSequence - seq.nextGroupMaxVal = _startSequence + seq.step - 1 - rs, err := tx.Exec(ctx, "", _initGroupSequence, proto.NewValueInt64(seq.nextGroupMaxVal+1), proto.NewValueInt64(seq.step), proto.NewValueString(seq.tableName)) + seq.currentVal = _startSequence + seq.currentGroupMaxVal = _startSequence + seq.step - 1 + rs, err := tx.Exec(ctx, "", _initGroupSequence, proto.NewValueInt64(_startSequence), proto.NewValueInt64(seq.step), proto.NewValueString(seq.tableName)) if err != nil { return err } @@ -216,20 +213,19 @@ func (seq *groupSequence) acquireNextGroup(ctx context.Context, rt runtime.Runti } return err } - if err = row.Scan(val); err != nil { + + if err = row.Scan(vals); err != nil { return err } _, _ = ds.Next() - if val[0] != nil { - nextGroupStartVal, _ := val[0].Int64() - if nextGroupStartVal%seq.step != 0 { - // padding left - nextGroupStartVal = (nextGroupStartVal/seq.step + 1) * seq.step - } - seq.nextGroupStartVal = nextGroupStartVal - seq.nextGroupMaxVal = seq.nextGroupStartVal + seq.step - 1 - rs, err := tx.Exec(ctx, "", _updateNextGroup, proto.NewValueInt64(seq.nextGroupMaxVal+1), proto.NewValueString(seq.tableName)) + if vals[0] != nil { + lastGroupStartVal, _ := vals[0].Int64() + // padding left + currentGroupStartVal := (lastGroupStartVal/seq.step+1)*seq.step + 1 + seq.currentVal = currentGroupStartVal + seq.currentGroupMaxVal = currentGroupStartVal + seq.step - 1 + rs, err := tx.Exec(ctx, "", _updateNextGroup, proto.NewValueInt64(currentGroupStartVal), proto.NewValueString(seq.tableName)) if err != nil { return err } diff --git a/pkg/sequence/group/group_test.go b/pkg/sequence/group/group_test.go index 9507417b..dd6af719 100644 --- a/pkg/sequence/group/group_test.go +++ b/pkg/sequence/group/group_test.go @@ -20,29 +20,95 @@ package group import ( "context" "fmt" + "io" "sync" "testing" ) import ( + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" ) +import ( + "github.com/arana-db/arana/pkg/proto" + "github.com/arana-db/arana/pkg/runtime" + "github.com/arana-db/arana/testdata" +) + +const ( + tenant = "fakeTenant" + schema = "employees" + tableName = "mock_group_sequence" +) + func Test_groupSequence_Acquire(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ctx := context.WithValue(context.Background(), proto.ContextKeyTenant{}, tenant) + ctx = context.WithValue(ctx, proto.ContextKeySchema{}, schema) + + // build mock row + mockRow := testdata.NewMockRow(ctrl) + mockRow.EXPECT().Scan(gomock.Any()).DoAndReturn(func(dest []proto.Value) error { + dest[0] = proto.NewValueInt64(1) + return nil + }) + + // build mock dataset + mockDataset := testdata.NewMockDataset(ctrl) + mockDataset.EXPECT().Next().Return(nil, io.EOF).Times(1) + mockDataset.EXPECT().Next().Return(mockRow, nil).Times(2) + + // build mock result + mockRes := testdata.NewMockResult(ctrl) + mockRes.EXPECT().RowsAffected().Return(uint64(0), nil).AnyTimes() + mockRes.EXPECT().Dataset().Return(mockDataset, nil).AnyTimes() + + // build mock transaction + mockTx := testdata.NewMockTx(ctrl) + mockTx.EXPECT().Exec(gomock.Any(), gomock.Any(), gomock.Eq(_initGroupSequenceTableSql)).Return(mockRes, nil).AnyTimes() + mockTx.EXPECT().Exec(gomock.Any(), gomock.Any(), gomock.Eq(_initGroupSequence), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockRes, nil).AnyTimes() + mockTx.EXPECT().Exec(gomock.Any(), gomock.Any(), gomock.Eq(_updateNextGroup), gomock.Any(), gomock.Any()).Return(mockRes, nil).AnyTimes() + mockTx.EXPECT().Query(gomock.Any(), gomock.Any(), gomock.Eq(_selectNextGroupWithXLock), gomock.Any()).Return(mockRes, nil).AnyTimes() + mockTx.EXPECT().Commit(gomock.Any()).Return(mockRes, uint16(0), nil).AnyTimes() + mockTx.EXPECT().Rollback(gomock.Any()).Return(mockRes, uint16(0), nil).AnyTimes() + + // build mock runtime + mockRt := runtime.NewMockRuntime(ctrl) + mockRt.EXPECT().Begin(gomock.Any()).Return(mockTx, nil).AnyTimes() + + runtime.Register(tenant, schema, mockRt) + + ctx = context.WithValue(ctx, proto.RuntimeCtxKey{}, mockRt) + + conf := proto.SequenceConfig{ + Name: "group", + Type: "group", + Option: map[string]string{_stepKey: "100"}, + } + + validate(t, ctx, conf, 0, 0, 1) + validate(t, ctx, conf, 100, 100, 101) +} + +func validate(t *testing.T, ctx context.Context, conf proto.SequenceConfig, currentVal, currentGroupMaxVal, expectVal int64) { seq := &groupSequence{ mu: sync.Mutex{}, - tableName: "mock_group_sequence", - step: 100, - currentGroupMaxVal: 99, - currentVal: 50, + tableName: tableName, + currentGroupMaxVal: currentGroupMaxVal, + currentVal: currentVal, } - val, err := seq.Acquire(context.Background()) + err := seq.Start(ctx, conf) + assert.NoError(t, err) + + val, err := seq.Acquire(ctx) assert.NoError(t, err, fmt.Sprintf("acquire err : %v", err)) curVal := seq.CurrentVal() - - assert.Equal(t, int64(51), curVal, fmt.Sprintf("acquire val: %d, cur val: %d", val, curVal)) - assert.Equal(t, val, curVal, fmt.Sprintf("acquire val: %d, cur val: %d", val, curVal)) + assert.Equal(t, expectVal, curVal, fmt.Sprintf("acquire val: %d, cur val: %d", val, curVal)) } diff --git a/pkg/sequence/snowflake/snowflake.go b/pkg/sequence/snowflake/snowflake.go index c79721de..c9ac97a8 100644 --- a/pkg/sequence/snowflake/snowflake.go +++ b/pkg/sequence/snowflake/snowflake.go @@ -248,8 +248,8 @@ func (seq *snowflakeSequence) GetSequenceConfig() proto.SequenceConfig { } } -func (seq *snowflakeSequence) findWorkID(ctx context.Context, tx proto.Tx, seqName string) (int64, error) { - ret, err := tx.Query(ctx, "", _selectSelfWorkIdWithXLock, proto.NewValueString(seqName), proto.NewValueString(_nodeId)) +func (seq *snowflakeSequence) findWorkID(ctx context.Context, tx proto.Tx, tableName string) (int64, error) { + ret, err := tx.Query(ctx, "", _selectSelfWorkIdWithXLock, proto.NewValueString(tableName), proto.NewValueString(_nodeId)) if err != nil { return 0, err } @@ -271,7 +271,7 @@ func (seq *snowflakeSequence) findWorkID(ctx context.Context, tx proto.Tx, seqNa } } - ret, err = tx.Query(ctx, "", _selectMaxWorkIdWithXLock, proto.NewValueString(seqName)) + ret, err = tx.Query(ctx, "", _selectMaxWorkIdWithXLock, proto.NewValueString(tableName)) if err != nil { return 0, err } @@ -297,7 +297,7 @@ func (seq *snowflakeSequence) findWorkID(ctx context.Context, tx proto.Tx, seqNa curId, _ := val[0].Int64() curId++ if curId > workIdMax { - ret, err := tx.Query(ctx, "", _selectFreeWorkIdWithXLock, proto.NewValueString(seqName)) + ret, err := tx.Query(ctx, "", _selectFreeWorkIdWithXLock, proto.NewValueString(tableName)) if err != nil { return 0, err } diff --git a/pkg/sequence/snowflake/snowflake_test.go b/pkg/sequence/snowflake/snowflake_test.go index 6f62bd60..96b15fe8 100644 --- a/pkg/sequence/snowflake/snowflake_test.go +++ b/pkg/sequence/snowflake/snowflake_test.go @@ -20,28 +20,81 @@ package snowflake import ( "context" "fmt" + "io" "sync" "testing" - "time" ) import ( + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" ) +import ( + "github.com/arana-db/arana/pkg/proto" + "github.com/arana-db/arana/pkg/runtime" + "github.com/arana-db/arana/testdata" +) + func Test_snowflakeSequence_Acquire(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // build mock row + mockRow := testdata.NewMockRow(ctrl) + mockRow.EXPECT().Scan(gomock.Any()).DoAndReturn(func(dest []proto.Value) error { + dest[0] = proto.NewValueInt64(1024) + return nil + }).AnyTimes() + + // build mock dataset + mockDataset := testdata.NewMockDataset(ctrl) + mockDataset.EXPECT().Next().Return(mockRow, nil).Times(2) + + // build mock result + mockRes := testdata.NewMockResult(ctrl) + mockRes.EXPECT().RowsAffected().Return(uint64(0), nil).AnyTimes() + mockRes.EXPECT().Dataset().Return(mockDataset, nil).AnyTimes() + + // build mock transaction + mockTx := testdata.NewMockTx(ctrl) + mockTx.EXPECT().Exec(gomock.Any(), gomock.Any(), gomock.Eq(_initTableSql)).Return(mockRes, nil).AnyTimes() + mockTx.EXPECT().Exec(gomock.Any(), gomock.Any(), gomock.Eq(_keepaliveNode), gomock.Any()).Return(mockRes, nil).AnyTimes() + mockTx.EXPECT().Exec(gomock.Any(), gomock.Any(), gomock.Eq(_setWorkId), gomock.Any(), gomock.Any(), gomock.Any()).Return(mockRes, nil).AnyTimes() + mockTx.EXPECT().Query(gomock.Any(), gomock.Any(), gomock.Eq(_selectSelfWorkIdWithXLock), gomock.Any(), gomock.Any()).Return(mockRes, nil).AnyTimes() + mockTx.EXPECT().Query(gomock.Any(), gomock.Any(), gomock.Eq(_selectMaxWorkIdWithXLock), gomock.Any()).Return(mockRes, nil).AnyTimes() + mockTx.EXPECT().Query(gomock.Any(), gomock.Any(), gomock.Eq(_selectFreeWorkIdWithXLock), gomock.Any()).Return(mockRes, nil).AnyTimes() + mockTx.EXPECT().Commit(gomock.Any()).Return(mockRes, uint16(0), nil).AnyTimes() + mockTx.EXPECT().Rollback(gomock.Any()).Return(mockRes, uint16(0), nil).AnyTimes() + + // build mock runtime + mockRt := runtime.NewMockRuntime(ctrl) + mockRt.EXPECT().Begin(gomock.Any()).Return(mockTx, nil).AnyTimes() + + ctx := context.WithValue(context.Background(), proto.RuntimeCtxKey{}, mockRt) + seq := &snowflakeSequence{ - mu: sync.Mutex{}, - epoch: startWallTime.Add(time.Unix(_defaultEpoch/1000, (_defaultEpoch%1000)*1000000).Sub(startWallTime)), - workId: 1, + mu: sync.Mutex{}, } - val, err := seq.Acquire(context.Background()) + conf := proto.SequenceConfig{ + Name: "sf-ut", + } + err := seq.Start(ctx, conf) + assert.NoError(t, err) + + finishInitTable = false + mockDataset.EXPECT().Next().Return(nil, io.EOF).Times(1) + mockDataset.EXPECT().Next().Return(mockRow, nil).AnyTimes() + err = seq.Start(ctx, conf) + expectErr := fmt.Errorf("node worker-id must in [0, %d]", workIdMax) + assert.Equal(t, expectErr, err) + val, err := seq.Acquire(context.Background()) assert.NoError(t, err, fmt.Sprintf("acquire err : %v", err)) curVal := seq.CurrentVal() - assert.Equal(t, val, curVal, fmt.Sprintf("acquire val: %d, cur val: %d", val, curVal)) bucket := []int64{0, 0}