mirror of https://github.com/dtm-labs/dtm.git
8 changed files with 171 additions and 52 deletions
@ -0,0 +1,73 @@ |
|||
package dtmcli |
|||
|
|||
import ( |
|||
"fmt" |
|||
|
|||
"github.com/dtm-labs/dtm/dtmcli/logger" |
|||
"github.com/go-redis/redis/v8" |
|||
) |
|||
|
|||
// RedisCheckAdjustAmount check the value of key is valid and >= amount. then adjust the amount
|
|||
func (bb *BranchBarrier) RedisCheckAdjustAmount(rd *redis.Client, key string, amount int, barrierExpire int) error { |
|||
bb.BarrierID = bb.BarrierID + 1 |
|||
bkey1 := fmt.Sprintf("%s-%s-%s-%02d", bb.Gid, bb.BranchID, bb.Op, bb.BarrierID) |
|||
originOp := map[string]string{ |
|||
BranchCancel: BranchTry, |
|||
BranchCompensate: BranchAction, |
|||
}[bb.Op] |
|||
bkey2 := fmt.Sprintf("%s-%s-%s-%02d", bb.Gid, bb.BranchID, originOp, bb.BarrierID) |
|||
v, err := rd.Eval(rd.Context(), ` -- RedisCheckAdjustAmount |
|||
local v = redis.call('GET', KEYS[1]) |
|||
local e1 = redis.call('GET', KEYS[2]) |
|||
|
|||
if v == false or v + ARGV[1] < 0 then |
|||
return 'FAILURE' |
|||
end |
|||
|
|||
if e1 ~= false then |
|||
return |
|||
end |
|||
|
|||
redis.call('SET', KEYS[2], 'op', 'EX', ARGV[3]) |
|||
|
|||
if ARGV[2] ~= '' then |
|||
local e2 = redis.call('GET', KEYS[3]) |
|||
if e2 == false then |
|||
redis.call('SET', KEYS[3], 'rollback', 'EX', ARGV[3]) |
|||
return |
|||
end |
|||
end |
|||
redis.call('INCRBY', KEYS[1], ARGV[1]) |
|||
`, []string{key, bkey1, bkey2}, amount, originOp, barrierExpire).Result() |
|||
logger.Debugf("lua return v: %v err: %v", v, err) |
|||
if err == redis.Nil { |
|||
err = nil |
|||
} |
|||
if err == nil && v == ResultFailure { |
|||
err = ErrFailure |
|||
} |
|||
return err |
|||
} |
|||
|
|||
// RedisQueryPrepared query prepared for redis
|
|||
func (bb *BranchBarrier) RedisQueryPrepared(rd *redis.Client, barrierExpire int) error { |
|||
bkey1 := fmt.Sprintf("%s-%s-%s-%s", bb.Gid, "00", "msg", "01") |
|||
v, err := rd.Eval(rd.Context(), ` -- RedisQueryPrepared |
|||
local v = redis.call('GET', KEYS[1]) |
|||
if v == false then |
|||
redis.call('SET', KEYS[1], 'rollback', 'EX', ARGV[1]) |
|||
v = 'rollback' |
|||
end |
|||
if v == 'rollback' then |
|||
return 'FAILURE' |
|||
end |
|||
`, []string{bkey1}, barrierExpire).Result() |
|||
logger.Debugf("lua return v: %v err: %v", v, err) |
|||
if err == redis.Nil { |
|||
err = nil |
|||
} |
|||
if err == nil && v == ResultFailure { |
|||
err = ErrFailure |
|||
} |
|||
return err |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
package test |
|||
|
|||
import ( |
|||
"errors" |
|||
"testing" |
|||
|
|||
"github.com/dtm-labs/dtm/dtmcli" |
|||
"github.com/dtm-labs/dtm/dtmcli/dtmimp" |
|||
"github.com/dtm-labs/dtm/test/busi" |
|||
"github.com/stretchr/testify/assert" |
|||
) |
|||
|
|||
func TestMsgRedisDo(t *testing.T) { |
|||
before := getBeforeBalances("redis") |
|||
gid := dtmimp.GetFuncName() |
|||
req := busi.GenTransReq(30, false, false) |
|||
msg := dtmcli.NewMsg(DtmServer, gid). |
|||
Add(busi.Busi+"/SagaRedisTransIn", req) |
|||
err := msg.DoAndSubmit(Busi+"/RedisQueryPrepared", func(bb *dtmcli.BranchBarrier) error { |
|||
return bb.RedisCheckAdjustAmount(busi.RedisGet(), busi.GetRedisAccountKey(busi.TransOutUID), -30, 86400) |
|||
}) |
|||
assert.Nil(t, err) |
|||
waitTransProcessed(msg.Gid) |
|||
assert.Equal(t, []string{StatusSucceed}, getBranchesStatus(msg.Gid)) |
|||
assert.Equal(t, StatusSucceed, getTransStatus(msg.Gid)) |
|||
assertNotSameBalance(t, before, "redis") |
|||
} |
|||
|
|||
func TestMsgRedisDoBusiFailed(t *testing.T) { |
|||
before := getBeforeBalances("redis") |
|||
gid := dtmimp.GetFuncName() |
|||
req := busi.GenTransReq(30, false, false) |
|||
msg := dtmcli.NewMsg(DtmServer, gid). |
|||
Add(busi.Busi+"/SagaRedisTransIn", req) |
|||
err := msg.DoAndSubmit(Busi+"/RedisQueryPrepared", func(bb *dtmcli.BranchBarrier) error { |
|||
return errors.New("an error") |
|||
}) |
|||
assert.Error(t, err) |
|||
assertSameBalance(t, before, "redis") |
|||
} |
|||
|
|||
func TestMsgRedisDoPrepareFailed(t *testing.T) { |
|||
before := getBeforeBalances("redis") |
|||
gid := dtmimp.GetFuncName() |
|||
req := busi.GenTransReq(30, false, false) |
|||
msg := dtmcli.NewMsg(DtmServer+"not-exists", gid). |
|||
Add(busi.Busi+"/SagaRedisTransIn", req) |
|||
err := msg.DoAndSubmit(Busi+"/RedisQueryPrepared", func(bb *dtmcli.BranchBarrier) error { |
|||
return bb.RedisCheckAdjustAmount(busi.RedisGet(), busi.GetRedisAccountKey(busi.TransOutUID), -30, 86400) |
|||
}) |
|||
assert.Error(t, err) |
|||
assertSameBalance(t, before, "redis") |
|||
} |
|||
|
|||
func TestMsgRedisDoCommitFailed(t *testing.T) { |
|||
before := getBeforeBalances("redis") |
|||
gid := dtmimp.GetFuncName() |
|||
req := busi.GenTransReq(30, false, false) |
|||
msg := dtmcli.NewMsg(DtmServer, gid). |
|||
Add(busi.Busi+"/SagaRedisTransIn", req) |
|||
err := msg.DoAndSubmit(Busi+"/RedisQueryPrepared", func(bb *dtmcli.BranchBarrier) error { |
|||
return errors.New("after commit error") |
|||
}) |
|||
assert.Error(t, err) |
|||
assertSameBalance(t, before, "redis") |
|||
} |
|||
|
|||
func TestMsgRedisDoCommitAfterFailed(t *testing.T) { |
|||
before := getBeforeBalances("redis") |
|||
gid := dtmimp.GetFuncName() |
|||
req := busi.GenTransReq(30, false, false) |
|||
msg := dtmcli.NewMsg(DtmServer, gid). |
|||
Add(busi.Busi+"/SagaRedisTransIn", req) |
|||
err := msg.DoAndSubmit(Busi+"/RedisQueryPrepared", func(bb *dtmcli.BranchBarrier) error { |
|||
err := bb.RedisCheckAdjustAmount(busi.RedisGet(), busi.GetRedisAccountKey(busi.TransOutUID), -30, 86400) |
|||
dtmimp.E2P(err) |
|||
return errors.New("an error") |
|||
}) |
|||
assert.Error(t, err) |
|||
waitTransProcessed(gid) |
|||
assertNotSameBalance(t, before, "redis") |
|||
} |
|||
Loading…
Reference in new issue