囲碁におけるTCC分散トランザクションのベストプラクティス
22705 ワード
この記事は、TCCタイプ取引の正確な理解を読者に与える完全なTCC例を提示するでしょう
典型的な分散トランザクションシナリオは、銀行間の資金を転送する必要がある銀行間転送である.仮説的要求シナリオは、AからBへの両方の転送が成功し、失敗する可能性があることである.
また、ロールバックがある場合、サガモードは、そのバランスが差し引かれたことを発見する結果となりますが、受取人、Bは、大きな苦痛を引き起こすバランスを受け取るのが遅いです.ビジネスはこの状況を避けることを好む
TCCは3部に分けられるトライパート:実行しようとすると、すべてのビジネスチェック(一貫性)を完了し、必要なビジネスリソースを予約します. 確認:すべての支店がトライフェーズで成功した場合、確認フェーズに移動します.確認フェーズでは、try - phase で予約されているビジネスリソースのみを使用して、ビジネスチェックなしで実際にビジネスを実行します.をキャンセルします:すべてのブランチのTrysの1つが失敗するならば、我々はキャンセル段階に行きます. 我々が別々のマイクロサービスのトランスアウトとトランスインで、我々が銀行インターバンク転送に類似したトランザクションを実行することになっているならば、正常に完成したTCCトランザクションのための典型的なタイミング図は以下の通りです.
私たちのソリューションは、分散トランザクションのソリューションに専用の優れたプロジェクトは、分散トランザクションフレームワークdtmに基づいています.
最初に、アカウントバランス表を作成します.ここで、tradingchen balanceは、凍結された量を示します.
それから、TCCトランザクションはつくられます、そして、分岐呼び出しは作られます
成功した例を実行したい場合は、次の手順に従います. DTMを実行する
例を実行する
DTMにコミットされたトランザクションがこれらのステップの1つで簡単に失敗すると仮定してください.DTMは不完全な操作を再試行します.そして、グローバルトランザクションの下位トランザクションを必要とします.DTMフレームワークは、サブトランザクションバリア技術を開拓しました.この関数の内部での動作を一度に一度呼び出すことを保証する関数コールを提供します.
銀行がユーザー2に量を移す準備をしていて、ユーザー2のアカウントが異常であるとわかるならば、何が起こるか、失敗を返しますか?この状況をシミュレートするコードを変更します.
これと成功したTCCの違いは、子トランザクションが失敗を返すと、その後グローバルトランザクションがロールバックされ、各トランザクションのキャンセル操作を呼び出してグローバルトランザクションがすべてロールバックされることを保証します.
transintryのフォワード操作は何もせずに失敗を返しました、この点でTransincancel補償操作を呼ぶことは逆の調整が間違って行く原因になります?
心配しないで、前の副トランザクションバリア技術は、それがコミットの前に起こるならば、transintry誤りがヌル操作として補償されるのを確実にします、そして、コミットの後、TransIntry誤りが起こるならば、補償操作はデータをコミットします.
あなたは
この記事は完全なTCCトランザクション解決を与えます.あなたはこの例にいくつかの簡単な変更を使用して実際の問題を解決するために使用することができます
TCCの原理の詳細については、TCC
プロジェクトを訪問する歓迎
ビジネスシナリオ
典型的な分散トランザクションシナリオは、銀行間の資金を転送する必要がある銀行間転送である.仮説的要求シナリオは、AからBへの両方の転送が成功し、失敗する可能性があることである.
また、ロールバックがある場合、サガモードは、そのバランスが差し引かれたことを発見する結果となりますが、受取人、Bは、大きな苦痛を引き起こすバランスを受け取るのが遅いです.ビジネスはこの状況を避けることを好む
TCCコンポーネント
TCCは3部に分けられる
私たちのソリューションは、分散トランザクションのソリューションに専用の優れたプロジェクトは、分散トランザクションフレームワークdtmに基づいています.
コアオペレーション
最初に、アカウントバランス表を作成します.ここで、tradingchen balanceは、凍結された量を示します.
create table if not exists dtm_busi.user_account(
id int(11) PRIMARY KEY AUTO_INCREMENT,
user_id int(11) UNIQUE,
balance DECIMAL(10, 2) not null default '0',
trading_balance DECIMAL(10, 2) not null default '0',
create_time datetime DEFAULT now(),
update_time datetime DEFAULT now(),
key(create_time),
key(update_time)
);
まず最初にコアコードを書きましょう.凍結/非凍結資金操作は、制約のバランスがとれていない場合+ tradingchen balance >= 0をチェックしますfunc tccAdjustTrading(db dtmcli.DB, uid int, amount int) error {
affected, err := dtmimp.DBExec(db, "update dtm_busi.user_account set trading_balance=trading_balance+? where user_id=? and trading_balance + ? + balance >= 0", amount, uid, amount)
if err == nil && affected == 0 {
return fmt.Errorf("update error, maybe balance not enough")
}
return err
}
func tccAdjustBalance(db dtmcli.DB, uid int, amount int) error {
affected, err := dtmimp.DBExec(db, "update dtm_busi.user_account set trading_balance=trading_balance-?, balance=balance+? where user_id=? ", amount, amount, uid)
if err == nil && affected == 0 {
return fmt.Errorf("update user_account 0 rows")
}
return err
}
特定のtry/確認/キャンセルハンドラ関数を書きましょうapp.POST(BusiAPI+"/TccBTransOutTry", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
bb := MustBarrierFromGin(c)
return bb.Call(txGet(), func(tx *sql.Tx) error {
return tccAdjustTrading(tx, TransOutUID, -req.Amount)
})
}))
app.POST(BusiAPI+"/TccBTransOutConfirm", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
bb := MustBarrierFromGin(c)
return bb.Call(txGet(), func(tx *sql.Tx) error {
return tccAdjustBalance(tx, TransOutUID, -reqFrom(c).Amount)
})
}))
app.POST(BusiAPI+"/TccBTransOutCancel", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
bb := MustBarrierFromGin(c)
return bb.Call(txGet(), func(tx *sql.Tx) error {
return tccAdjustTrading(tx, TransOutUID, req.Amount)
})
}))
app.POST(BusiAPI+"/TccBTransInTry", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
bb := MustBarrierFromGin(c)
return bb.Call(txGet(), func(tx *sql.Tx) error {
return tccAdjustTrading(tx, TransInUID, req.Amount)
})
}))
app.POST(BusiAPI+"/TccBTransOutConfirm", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
bb := MustBarrierFromGin(c)
return bb.Call(txGet(), func(tx *sql.Tx) error {
return tccAdjustBalance(tx, TransInUID, reqFrom(c).Amount)
})
}))
app.POST(BusiAPI+"/TccBTransInCancel", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
bb := MustBarrierFromGin(c)
return bb.Call(txGet(), func(tx *sql.Tx) error {
return tccAdjustTrading(tx, TransInUID, -req.Amount)
})
}))
これらの関数のコアロジックは、バランスを凍結調整することですTCC取引
それから、TCCトランザクションはつくられます、そして、分岐呼び出しは作られます
// TccGlobalTransaction will open a global transaction
_, err := dtmcli.TccGlobalTransaction(DtmServer, func(tcc *dtmcli.Tcc) (rerr error) {
// CallBranch will register the Confirm/Cancel of the transaction branch to the global transaction, and then call Try directly
res1, rerr := tcc.CallBranch(&TransReq{Amount: 30}, host+"/api/TccBTransOutTry", host+"/api/TccBTransOutConfirm", host+"/api/ TccBTransOutCancel"
if err ! = nil {
return resp, err
}
return tcc.CallBranch(&TransReq{Amount: 30}, host+"/api/TccBTransInTry", host+"/api/TccBTransInConfirm", host+"/api/TccBTransInCancel")
})
この時点で、完全なTCC分散トランザクションが終了します.ラン
成功した例を実行したい場合は、次の手順に従います.
git clone https://github.com/dtm-labs/dtm && cd dtm
go run main.go
git clone https://github.com/dtm-labs/dtm-examples && cd dtm-examples
go run main.go http_tcc_barrier
ネットワーク例外の処理
DTMにコミットされたトランザクションがこれらのステップの1つで簡単に失敗すると仮定してください.DTMは不完全な操作を再試行します.そして、グローバルトランザクションの下位トランザクションを必要とします.DTMフレームワークは、サブトランザクションバリア技術を開拓しました.この関数の内部での動作を一度に一度呼び出すことを保証する関数コールを提供します.
func (bb *BranchBarrier) Call(tx *sql.Tx, busiCall BarrierBusiFunc) error
このブランチバリアは、自動的に無効化だけでなく、ヌル補償と絞り込み問題を扱うことができます.詳細についてはexceptions and solutionsを参照してください.TCCのロールバック
銀行がユーザー2に量を移す準備をしていて、ユーザー2のアカウントが異常であるとわかるならば、何が起こるか、失敗を返しますか?この状況をシミュレートするコードを変更します.
app.POST(BusiAPI+"/TccBTransInTry", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
return dtmcli.ErrFailure
}))
これはトランザクション障害の相互作用のタイミング図ですこれと成功したTCCの違いは、子トランザクションが失敗を返すと、その後グローバルトランザクションがロールバックされ、各トランザクションのキャンセル操作を呼び出してグローバルトランザクションがすべてロールバックされることを保証します.
transintryのフォワード操作は何もせずに失敗を返しました、この点でTransincancel補償操作を呼ぶことは逆の調整が間違って行く原因になります?
心配しないで、前の副トランザクションバリア技術は、それがコミットの前に起こるならば、transintry誤りがヌル操作として補償されるのを確実にします、そして、コミットの後、TransIntry誤りが起こるならば、補償操作はデータをコミットします.
あなたは
bb.Call
に変更することができますapp.POST(BusiAPI+"/TccBTransInTry", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
bb := MustBarrierFromGin(c)
bb.Call(txGet(), func(tx *sql.Tx) error {
return tccAdjustTrading(tx, TransInUID, req.Amount)
})
return dtmcli.ErrFailure
}))
最終的な結果のバランスはまだ正しいでしょう、詳細はExceptions and Solutionsを見てください.概要
この記事は完全なTCCトランザクション解決を与えます.あなたはこの例にいくつかの簡単な変更を使用して実際の問題を解決するために使用することができます
TCCの原理の詳細については、TCC
プロジェクトを訪問する歓迎
Reference
この問題について(囲碁におけるTCC分散トランザクションのベストプラクティス), 我々は、より多くの情報をここで見つけました https://dev.to/yedf2/best-practice-for-tcc-distributed-transaction-in-go-402mテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol