You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

tx.go 4.7 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package sql
  18. import (
  19. "context"
  20. "database/sql/driver"
  21. "fmt"
  22. "sync"
  23. "time"
  24. "seata.apache.org/seata-go/pkg/datasource/sql/datasource"
  25. "seata.apache.org/seata-go/pkg/datasource/sql/types"
  26. "seata.apache.org/seata-go/pkg/protocol/branch"
  27. "seata.apache.org/seata-go/pkg/rm"
  28. "seata.apache.org/seata-go/pkg/util/backoff"
  29. "seata.apache.org/seata-go/pkg/util/log"
  30. )
  31. var (
  32. hl sync.RWMutex
  33. txHooks []txHook
  34. )
  35. func RegisterTxHook(h txHook) {
  36. hl.Lock()
  37. defer hl.Unlock()
  38. txHooks = append(txHooks, h)
  39. }
  40. func CleanTxHooks() {
  41. hl.Lock()
  42. defer hl.Unlock()
  43. txHooks = make([]txHook, 0, 4)
  44. }
  45. type (
  46. txOption func(tx *Tx)
  47. txHook interface {
  48. BeforeCommit(tx *Tx)
  49. BeforeRollback(tx *Tx)
  50. }
  51. )
  52. func newTx(opts ...txOption) (driver.Tx, error) {
  53. tx := new(Tx)
  54. for i := range opts {
  55. opts[i](tx)
  56. }
  57. if err := tx.init(); err != nil {
  58. return nil, err
  59. }
  60. return tx, nil
  61. }
  62. // withDriverConn
  63. func withDriverConn(conn *Conn) txOption {
  64. return func(t *Tx) {
  65. t.conn = conn
  66. }
  67. }
  68. // withOriginTx
  69. func withOriginTx(tx driver.Tx) txOption {
  70. return func(t *Tx) {
  71. t.target = tx
  72. }
  73. }
  74. // withTxCtx
  75. func withTxCtx(ctx *types.TransactionContext) txOption {
  76. return func(t *Tx) {
  77. t.tranCtx = ctx
  78. }
  79. }
  80. // Tx
  81. type Tx struct {
  82. conn *Conn
  83. tranCtx *types.TransactionContext
  84. target driver.Tx
  85. }
  86. // Commit do commit action
  87. func (tx *Tx) Commit() error {
  88. tx.beforeCommit()
  89. return tx.commitOnLocal()
  90. }
  91. func (tx *Tx) beforeCommit() {
  92. if len(txHooks) != 0 {
  93. hl.RLock()
  94. defer hl.RUnlock()
  95. for i := range txHooks {
  96. txHooks[i].BeforeCommit(tx)
  97. }
  98. }
  99. }
  100. func (tx *Tx) Rollback() error {
  101. if len(txHooks) != 0 {
  102. hl.RLock()
  103. defer hl.RUnlock()
  104. for i := range txHooks {
  105. txHooks[i].BeforeRollback(tx)
  106. }
  107. }
  108. return tx.target.Rollback()
  109. }
  110. // init
  111. func (tx *Tx) init() error {
  112. return nil
  113. }
  114. // commitOnLocal
  115. func (tx *Tx) commitOnLocal() error {
  116. return tx.target.Commit()
  117. }
  118. // register
  119. func (tx *Tx) register(ctx *types.TransactionContext) error {
  120. if ctx.TransactionMode.BranchType() == branch.BranchTypeUnknow {
  121. return nil
  122. }
  123. if ctx.TransactionMode.BranchType() == branch.BranchTypeAT {
  124. if !ctx.HasUndoLog() || !ctx.HasLockKey() {
  125. return nil
  126. }
  127. }
  128. request := rm.BranchRegisterParam{
  129. Xid: ctx.XID,
  130. BranchType: ctx.TransactionMode.BranchType(),
  131. ResourceId: ctx.ResourceID,
  132. }
  133. var lockKey string
  134. if ctx.TransactionMode == types.ATMode {
  135. if !ctx.HasUndoLog() || !ctx.HasLockKey() {
  136. return nil
  137. }
  138. for k, _ := range ctx.LockKeys {
  139. lockKey += k + ";"
  140. }
  141. request.LockKeys = lockKey
  142. }
  143. dataSourceManager := datasource.GetDataSourceManager(ctx.TransactionMode.BranchType())
  144. branchId, err := dataSourceManager.BranchRegister(context.Background(), request)
  145. if err != nil {
  146. log.Errorf("Failed to register branch: %s", err.Error())
  147. return err
  148. }
  149. ctx.BranchID = uint64(branchId)
  150. return nil
  151. }
  152. // report
  153. func (tx *Tx) report(success bool) error {
  154. if tx.tranCtx.BranchID == 0 {
  155. return nil
  156. }
  157. status := getStatus(success)
  158. request := rm.BranchReportParam{
  159. Xid: tx.tranCtx.XID,
  160. BranchId: int64(tx.tranCtx.BranchID),
  161. Status: status,
  162. }
  163. dataSourceManager := datasource.GetDataSourceManager(tx.tranCtx.TransactionMode.BranchType())
  164. if dataSourceManager == nil {
  165. return fmt.Errorf("get dataSourceManager failed")
  166. }
  167. retry := backoff.New(context.Background(), backoff.Config{
  168. MinBackoff: 100 * time.Millisecond,
  169. MaxBackoff: 200 * time.Millisecond,
  170. MaxRetries: 5,
  171. })
  172. var err error
  173. for retry.Ongoing() {
  174. if err = dataSourceManager.BranchReport(context.Background(), request); err == nil {
  175. break
  176. }
  177. log.Infof("Failed to report [%s / %s] commit done [%s] Retry Countdown: %s", tx.tranCtx.BranchID, tx.tranCtx.XID, success, retry)
  178. retry.Wait()
  179. }
  180. return err
  181. }
  182. func getStatus(success bool) branch.BranchStatus {
  183. if success {
  184. return branch.BranchStatusPhaseoneDone
  185. } else {
  186. return branch.BranchStatusPhaseoneFailed
  187. }
  188. }