-
Notifications
You must be signed in to change notification settings - Fork 285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: saga's tcc-like mode #651
Open
kaori-seasons
wants to merge
11
commits into
apache:feature/saga
Choose a base branch
from
kaori-seasons:feature/saga-tcc-annotation
base: feature/saga
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,255
−7
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
55055df
init saga proxy
kaori-seasons 2ce221e
fix bug && rename
kaori-seasons 4370a7c
add fence definition
kaori-seasons 24cc9d0
add fence definition
kaori-seasons 28f38c2
remove useless code
kaori-seasons 57ab337
refactor code && add test
kaori-seasons f81784d
Merge branch 'feature/saga' into feature/saga-tcc-annotation
lovepoem 338e6bf
add license && fmt
kaori-seasons 9cf3c19
Merge remote-tracking branch 'origin/feature/saga-tcc-annotation' int…
kaori-seasons e435eef
recover code
kaori-seasons 7495cb7
Merge branch 'feature/saga' into feature/saga-tcc-annotation
luky116 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package fence | ||
|
||
import ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 格式太乱了 |
||
"context" | ||
"database/sql" | ||
"fmt" | ||
"github.com/seata/seata-go/pkg/rm/saga/fence/handler" | ||
"github.com/seata/seata-go/pkg/rm/tcc/fence/enum" | ||
"github.com/seata/seata-go/pkg/tm" | ||
) | ||
|
||
// WithFence Execute the fence database operation first and then call back the business method | ||
func WithFence(ctx context.Context, tx *sql.Tx, callback func() error) (err error) { | ||
if err = DoFence(ctx, tx); err != nil { | ||
return err | ||
} | ||
|
||
if err := callback(); err != nil { | ||
return fmt.Errorf("the business method error msg of: %p, [%w]", callback, err) | ||
} | ||
|
||
return | ||
} | ||
|
||
// DeFence This method is a suspended API interface that asserts the phase timing of a transaction | ||
// and performs corresponding database operations to ensure transaction consistency | ||
// case 1: if fencePhase is FencePhaseNotExist, will return a fence not found error. | ||
// case 2: if fencePhase is FencePhaseAction, will do commit fence operation. | ||
// case 3: if fencePhase is FencePhaseCompensationAction, will do rollback fence operation. | ||
// case 4: if fencePhase not in above case, will return a fence phase illegal error. | ||
func DoFence(ctx context.Context, tx *sql.Tx) error { | ||
hd := handler.GetSagaFenceHandler() | ||
phase := tm.GetFencePhase(ctx) | ||
|
||
switch phase { | ||
case enum.FencePhaseNotExist: | ||
return fmt.Errorf("xid %s, tx name %s, fence phase not exist", tm.GetXID(ctx), tm.GetTxName(ctx)) | ||
case enum.FencePhaseAction: | ||
return hd.ActionFence(ctx, tx) | ||
case enum.FencePhaseCompensationAction: | ||
return hd.CompensationFence(ctx, tx) | ||
} | ||
|
||
return fmt.Errorf("fence phase: %v illegal", phase) | ||
} |
166 changes: 166 additions & 0 deletions
166
pkg/rm/saga/fence/handler/saga_fence_wrapper_handler.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package handler | ||
|
||
import ( | ||
kaori-seasons marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"container/list" | ||
"context" | ||
"database/sql" | ||
"fmt" | ||
"github.com/seata/seata-go/pkg/rm/tcc/fence/enum" | ||
"github.com/seata/seata-go/pkg/rm/tcc/fence/store/db/dao" | ||
"github.com/seata/seata-go/pkg/rm/tcc/fence/store/db/model" | ||
"github.com/seata/seata-go/pkg/tm" | ||
"github.com/seata/seata-go/pkg/util/log" | ||
"sync" | ||
"time" | ||
) | ||
|
||
var ( | ||
fenceHandler *sagaFenceWrapperHandler | ||
fenceOnce sync.Once | ||
) | ||
|
||
const ( | ||
maxQueueSize = 500 | ||
) | ||
|
||
type sagaFenceWrapperHandler struct { | ||
tccFenceDao dao.TCCFenceStore | ||
logQueue chan *FenceLogIdentity | ||
logCache list.List | ||
logQueueOnce sync.Once | ||
logQueueCloseOnce sync.Once | ||
} | ||
|
||
type FenceLogIdentity struct { | ||
xid string | ||
branchId int64 | ||
} | ||
|
||
func GetSagaFenceHandler() *sagaFenceWrapperHandler { | ||
if fenceHandler == nil { | ||
fenceOnce.Do(func() { | ||
fenceHandler = &sagaFenceWrapperHandler{ | ||
tccFenceDao: dao.GetTccFenceStoreDatabaseMapper(), | ||
} | ||
}) | ||
} | ||
return fenceHandler | ||
} | ||
|
||
func (handler *sagaFenceWrapperHandler) ActionFence(ctx context.Context, tx *sql.Tx) error { | ||
xid := tm.GetBusinessActionContext(ctx).Xid | ||
branchId := tm.GetBusinessActionContext(ctx).BranchId | ||
|
||
fenceDo, err := handler.tccFenceDao.QueryTCCFenceDO(tx, xid, branchId) | ||
if err != nil { | ||
return fmt.Errorf(" commit fence method failed. xid= %s, branchId= %d, [%w]", xid, branchId, err) | ||
} | ||
if fenceDo == nil { | ||
return fmt.Errorf("tcc fence record not exists, commit fence method failed. xid= %s, branchId= %d", xid, branchId) | ||
} | ||
|
||
if fenceDo.Status == enum.StatusCommitted { | ||
log.Infof("branch transaction has already committed before. idempotency rejected. xid: %s, branchId: %d, status: %d", xid, branchId, fenceDo.Status) | ||
return nil | ||
} | ||
if fenceDo.Status == enum.StatusRollbacked || fenceDo.Status == enum.StatusSuspended { | ||
// enable warn level | ||
log.Warnf("branch transaction status is unexpected. xid: %s, branchId: %d, status: %s", xid, branchId, fenceDo.Status) | ||
return fmt.Errorf("branch transaction status is unexpected. xid: %s, branchId: %d, status: %d", xid, branchId, fenceDo.Status) | ||
} | ||
|
||
return handler.updateFenceStatus(tx, xid, branchId, enum.StatusCommitted) | ||
} | ||
|
||
func (handler *sagaFenceWrapperHandler) CompensationFence(ctx context.Context, tx *sql.Tx) error { | ||
xid := tm.GetBusinessActionContext(ctx).Xid | ||
branchId := tm.GetBusinessActionContext(ctx).BranchId | ||
actionName := tm.GetBusinessActionContext(ctx).ActionName | ||
fenceDo, err := handler.tccFenceDao.QueryTCCFenceDO(tx, xid, branchId) | ||
if err != nil { | ||
return fmt.Errorf("rollback fence method failed. xid= %s, branchId= %d, [%w]", xid, branchId, err) | ||
} | ||
|
||
// record is null, mean the need suspend | ||
if fenceDo == nil { | ||
err = handler.insertSagaFenceLog(tx, xid, branchId, actionName, enum.StatusSuspended) | ||
if err != nil { | ||
return fmt.Errorf("insert tcc fence record errors, rollback fence failed. xid= %s, branchId= %d, [%w]", xid, branchId, err) | ||
} | ||
log.Infof("Insert tcc fence suspend record xid: %s, branchId: %d", xid, branchId) | ||
return nil | ||
} | ||
|
||
// have rollbacked or suspended | ||
if fenceDo.Status == enum.StatusRollbacked || fenceDo.Status == enum.StatusSuspended { | ||
// enable warn level | ||
log.Infof("Branch transaction had already rollbacked before, idempotency rejected. xid: %s, branchId: %d, status: %s", xid, branchId, fenceDo.Status) | ||
return nil | ||
} | ||
if fenceDo.Status == enum.StatusCommitted { | ||
log.Warnf("Branch transaction status is unexpected. xid: %s, branchId: %d, status: %d", xid, branchId, fenceDo.Status) | ||
return fmt.Errorf("branch transaction status is unexpected. xid: %s, branchId: %d, status: %d", xid, branchId, fenceDo.Status) | ||
} | ||
|
||
return handler.updateFenceStatus(tx, xid, branchId, enum.StatusRollbacked) | ||
} | ||
|
||
func (handler *sagaFenceWrapperHandler) insertSagaFenceLog(tx *sql.Tx, xid string, branchId int64, actionName string, status enum.FenceStatus) error { | ||
tccFenceDo := model.TCCFenceDO{ | ||
Xid: xid, | ||
BranchId: branchId, | ||
ActionName: actionName, | ||
Status: status, | ||
} | ||
return handler.tccFenceDao.InsertTCCFenceDO(tx, &tccFenceDo) | ||
} | ||
|
||
func (handler *sagaFenceWrapperHandler) updateFenceStatus(tx *sql.Tx, xid string, branchId int64, status enum.FenceStatus) error { | ||
return handler.tccFenceDao.UpdateTCCFenceDO(tx, xid, branchId, enum.StatusTried, status) | ||
} | ||
|
||
func (handler *sagaFenceWrapperHandler) InitLogCleanChannel() { | ||
handler.logQueueOnce.Do(func() { | ||
go handler.traversalCleanChannel() | ||
}) | ||
} | ||
|
||
func (handler *sagaFenceWrapperHandler) DestroyLogCleanChannel() { | ||
handler.logQueueCloseOnce.Do(func() { | ||
close(handler.logQueue) | ||
}) | ||
} | ||
|
||
func (handler *sagaFenceWrapperHandler) deleteFence(xid string, id int64) error { | ||
// todo implement | ||
return nil | ||
} | ||
|
||
func (handler *sagaFenceWrapperHandler) deleteFenceByDate(datetime time.Time) int32 { | ||
// todo implement | ||
return 0 | ||
} | ||
|
||
func (handler *sagaFenceWrapperHandler) pushCleanChannel(xid string, branchId int64) { | ||
// todo implement | ||
fli := &FenceLogIdentity{ | ||
xid: xid, | ||
branchId: branchId, | ||
} | ||
select { | ||
case handler.logQueue <- fli: | ||
// todo add batch delete from log cache. | ||
default: | ||
handler.logCache.PushBack(fli) | ||
} | ||
log.Infof("add one log to clean queue: %v ", fli) | ||
} | ||
|
||
func (handler *sagaFenceWrapperHandler) traversalCleanChannel() { | ||
handler.logQueue = make(chan *FenceLogIdentity, maxQueueSize) | ||
for li := range handler.logQueue { | ||
if err := handler.deleteFence(li.xid, li.branchId); err != nil { | ||
log.Errorf("delete fence log failed, xid: %s, branchId: &s", li.xid, li.branchId) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
package saga |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
格式不对,挪到第三课 import 里面