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