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.

conn_xa.go 10 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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. gosql "database/sql"
  21. "database/sql/driver"
  22. "errors"
  23. "fmt"
  24. "time"
  25. "seata.apache.org/seata-go/pkg/datasource/sql/types"
  26. "seata.apache.org/seata-go/pkg/datasource/sql/xa"
  27. "seata.apache.org/seata-go/pkg/tm"
  28. "seata.apache.org/seata-go/pkg/util/log"
  29. )
  30. var xaConnTimeout time.Duration
  31. // XAConn Database connection proxy object under XA transaction model
  32. // Conn is assumed to be stateful.
  33. type XAConn struct {
  34. *Conn
  35. tx driver.Tx
  36. xaResource xa.XAResource
  37. xaBranchXid *XABranchXid
  38. xaActive bool
  39. rollBacked bool
  40. branchRegisterTime time.Time
  41. prepareTime time.Time
  42. isConnKept bool
  43. }
  44. func (c *XAConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
  45. if c.createOnceTxContext(ctx) {
  46. defer func() {
  47. c.txCtx = types.NewTxCtx()
  48. }()
  49. }
  50. //ret, err := c.createNewTxOnExecIfNeed(ctx, func() (types, error) {
  51. // ret, err := c.Conn.PrepareContext(ctx, query)
  52. // if err != nil {
  53. // return nil, err
  54. // }
  55. // return types.NewResult(types.WithRows(ret)), nil
  56. //})
  57. return c.Conn.PrepareContext(ctx, query)
  58. }
  59. // QueryContext exec xa sql
  60. func (c *XAConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
  61. if c.createOnceTxContext(ctx) {
  62. defer func() {
  63. c.txCtx = types.NewTxCtx()
  64. }()
  65. }
  66. ret, err := c.createNewTxOnExecIfNeed(ctx, func() (types.ExecResult, error) {
  67. ret, err := c.Conn.QueryContext(ctx, query, args)
  68. if err != nil {
  69. return nil, err
  70. }
  71. return types.NewResult(types.WithRows(ret)), nil
  72. })
  73. if err != nil {
  74. return nil, err
  75. }
  76. return ret.GetRows(), nil
  77. }
  78. func (c *XAConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
  79. if c.createOnceTxContext(ctx) {
  80. defer func() {
  81. c.txCtx = types.NewTxCtx()
  82. }()
  83. }
  84. ret, err := c.createNewTxOnExecIfNeed(ctx, func() (types.ExecResult, error) {
  85. ret, err := c.Conn.ExecContext(ctx, query, args)
  86. if err != nil {
  87. return nil, err
  88. }
  89. return types.NewResult(types.WithResult(ret)), nil
  90. })
  91. if err != nil {
  92. return nil, err
  93. }
  94. return ret.GetResult(), nil
  95. }
  96. // BeginTx like common transaction. but it just exec XA START
  97. func (c *XAConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
  98. if !tm.IsGlobalTx(ctx) {
  99. tx, err := c.Conn.BeginTx(ctx, opts)
  100. return tx, err
  101. }
  102. c.autoCommit = false
  103. c.txCtx = types.NewTxCtx()
  104. c.txCtx.DBType = c.res.dbType
  105. c.txCtx.TxOpt = opts
  106. c.txCtx.ResourceID = c.res.resourceID
  107. c.txCtx.XID = tm.GetXID(ctx)
  108. c.txCtx.TransactionMode = types.XAMode
  109. tx, err := c.Conn.BeginTx(ctx, opts)
  110. if err != nil {
  111. return nil, err
  112. }
  113. c.tx = tx
  114. if !c.autoCommit {
  115. if c.xaActive {
  116. return nil, errors.New("should NEVER happen: setAutoCommit from true to false while xa branch is active")
  117. }
  118. baseTx, ok := tx.(*Tx)
  119. if !ok {
  120. return nil, fmt.Errorf("start xa %s transaction failure for the tx is a wrong type", c.txCtx.XID)
  121. }
  122. c.branchRegisterTime = time.Now()
  123. if err := baseTx.register(c.txCtx); err != nil {
  124. c.cleanXABranchContext()
  125. return nil, fmt.Errorf("failed to register xa branch %s, err:%w", c.txCtx.XID, err)
  126. }
  127. c.xaBranchXid = XaIdBuild(c.txCtx.XID, c.txCtx.BranchID)
  128. c.keepIfNecessary()
  129. if err = c.start(ctx); err != nil {
  130. c.cleanXABranchContext()
  131. return nil, fmt.Errorf("failed to start xa branch xid:%s err:%w", c.txCtx.XID, err)
  132. }
  133. c.xaActive = true
  134. }
  135. return &XATx{tx: tx.(*Tx)}, nil
  136. }
  137. func (c *XAConn) createOnceTxContext(ctx context.Context) bool {
  138. onceTx := tm.IsGlobalTx(ctx) && c.autoCommit
  139. if onceTx {
  140. c.txCtx = types.NewTxCtx()
  141. c.txCtx.DBType = c.res.dbType
  142. c.txCtx.ResourceID = c.res.resourceID
  143. c.txCtx.XID = tm.GetXID(ctx)
  144. c.txCtx.TransactionMode = types.XAMode
  145. c.txCtx.GlobalLockRequire = true
  146. }
  147. return onceTx
  148. }
  149. func (c *XAConn) createNewTxOnExecIfNeed(ctx context.Context, f func() (types.ExecResult, error)) (types.ExecResult, error) {
  150. var (
  151. tx driver.Tx
  152. err error
  153. )
  154. currentAutoCommit := c.autoCommit
  155. if c.txCtx.TransactionMode != types.Local && tm.IsGlobalTx(ctx) && c.autoCommit {
  156. tx, err = c.BeginTx(ctx, driver.TxOptions{Isolation: driver.IsolationLevel(gosql.LevelDefault)})
  157. if err != nil {
  158. return nil, err
  159. }
  160. }
  161. defer func() {
  162. recoverErr := recover()
  163. if err != nil || recoverErr != nil {
  164. log.Errorf("conn at rollback error:%v or recoverErr:%v", err, recoverErr)
  165. if c.tx != nil {
  166. rollbackErr := c.tx.Rollback()
  167. if rollbackErr != nil {
  168. log.Errorf("conn at rollback error:%v", rollbackErr)
  169. }
  170. }
  171. }
  172. }()
  173. // execute SQL
  174. ret, err := f()
  175. if err != nil {
  176. // XA End & Rollback
  177. if rollbackErr := c.Rollback(ctx); rollbackErr != nil {
  178. log.Errorf("failed to rollback xa branch of :%s, err:%w", c.txCtx.XID, rollbackErr)
  179. }
  180. return nil, err
  181. }
  182. if tx != nil && currentAutoCommit {
  183. if err := c.Commit(ctx); err != nil {
  184. log.Errorf("xa connection proxy commit failure xid:%s, err:%v", c.txCtx.XID, err)
  185. // XA End & Rollback
  186. if err := c.Rollback(ctx); err != nil {
  187. log.Errorf("xa connection proxy rollback failure xid:%s, err:%v", c.txCtx.XID, err)
  188. }
  189. }
  190. }
  191. return ret, nil
  192. }
  193. func (c *XAConn) keepIfNecessary() {
  194. if c.ShouldBeHeld() {
  195. if err := c.res.Hold(c.xaBranchXid.String(), c); err == nil {
  196. c.isConnKept = true
  197. }
  198. }
  199. }
  200. func (c *XAConn) releaseIfNecessary() {
  201. if c.ShouldBeHeld() && c.xaBranchXid.String() != "" {
  202. if c.isConnKept {
  203. c.res.Release(c.xaBranchXid.String())
  204. c.isConnKept = false
  205. }
  206. }
  207. }
  208. func (c *XAConn) start(ctx context.Context) error {
  209. xaResource, err := xa.CreateXAResource(c.Conn.targetConn, c.dbType)
  210. if err != nil {
  211. return fmt.Errorf("create xa xid:%s resoruce err:%w", c.txCtx.XID, err)
  212. }
  213. c.xaResource = xaResource
  214. if err := c.xaResource.Start(ctx, c.xaBranchXid.String(), xa.TMNoFlags); err != nil {
  215. return fmt.Errorf("xa xid %s resource connection start err:%w", c.txCtx.XID, err)
  216. }
  217. if err := c.termination(c.xaBranchXid.String()); err != nil {
  218. c.xaResource.End(ctx, c.xaBranchXid.String(), xa.TMFail)
  219. c.XaRollback(ctx, c.xaBranchXid)
  220. return err
  221. }
  222. return err
  223. }
  224. func (c *XAConn) end(ctx context.Context, flags int) error {
  225. err := c.xaResource.End(ctx, c.xaBranchXid.String(), flags)
  226. if err != nil {
  227. return err
  228. }
  229. err = c.termination(c.xaBranchXid.String())
  230. if err != nil {
  231. return err
  232. }
  233. return nil
  234. }
  235. func (c *XAConn) termination(xaBranchXid string) error {
  236. branchStatus, err := branchStatus(xaBranchXid)
  237. if err != nil {
  238. c.releaseIfNecessary()
  239. return fmt.Errorf("failed xa branch [%v] the global transaction has finish, branch status: [%v]", c.txCtx.XID, branchStatus)
  240. }
  241. return nil
  242. }
  243. func (c *XAConn) cleanXABranchContext() {
  244. h, _ := time.ParseDuration("-1000h")
  245. c.branchRegisterTime = time.Now().Add(h)
  246. c.prepareTime = time.Now().Add(h)
  247. c.xaActive = false
  248. if !c.isConnKept {
  249. c.xaBranchXid = nil
  250. }
  251. }
  252. func (c *XAConn) Rollback(ctx context.Context) error {
  253. if c.autoCommit {
  254. return nil
  255. }
  256. if !c.xaActive || c.xaBranchXid == nil {
  257. return fmt.Errorf("should NOT rollback on an inactive session")
  258. }
  259. if !c.rollBacked {
  260. if c.xaResource.End(ctx, c.xaBranchXid.String(), xa.TMFail) != nil {
  261. return c.rollbackErrorHandle()
  262. }
  263. if c.XaRollback(ctx, c.xaBranchXid) != nil {
  264. c.cleanXABranchContext()
  265. return c.rollbackErrorHandle()
  266. }
  267. if err := c.tx.Rollback(); err != nil {
  268. c.cleanXABranchContext()
  269. return fmt.Errorf("failed to report XA branch commit-failure on xid:%s err:%w", c.txCtx.XID, err)
  270. }
  271. }
  272. c.cleanXABranchContext()
  273. return nil
  274. }
  275. func (c *XAConn) rollbackErrorHandle() error {
  276. return fmt.Errorf("failed to end(TMFAIL) xa branch on [%v] - [%v]", c.txCtx.XID, c.xaBranchXid.GetBranchId())
  277. }
  278. func (c *XAConn) Commit(ctx context.Context) error {
  279. if c.autoCommit {
  280. return nil
  281. }
  282. if !c.xaActive || c.xaBranchXid == nil {
  283. return fmt.Errorf("should NOT commit on an inactive session")
  284. }
  285. now := time.Now()
  286. if c.end(ctx, xa.TMSuccess) != nil {
  287. return c.commitErrorHandle(ctx)
  288. }
  289. if c.checkTimeout(ctx, now) != nil {
  290. return c.commitErrorHandle(ctx)
  291. }
  292. if c.xaResource.XAPrepare(ctx, c.xaBranchXid.String()) != nil {
  293. return c.commitErrorHandle(ctx)
  294. }
  295. return nil
  296. }
  297. func (c *XAConn) commitErrorHandle(ctx context.Context) error {
  298. var err error
  299. if err = c.XaRollback(ctx, c.xaBranchXid); err != nil {
  300. err = fmt.Errorf("failed to report XA branch commit-failure xid:%s, err:%w", c.txCtx.XID, err)
  301. }
  302. c.cleanXABranchContext()
  303. return err
  304. }
  305. func (c *XAConn) ShouldBeHeld() bool {
  306. return c.res.IsShouldBeHeld() || (c.res.GetDbType().String() != "" && c.res.GetDbType() != types.DBTypeUnknown)
  307. }
  308. func (c *XAConn) checkTimeout(ctx context.Context, now time.Time) error {
  309. if now.Sub(c.branchRegisterTime) > xaConnTimeout {
  310. c.XaRollback(ctx, c.xaBranchXid)
  311. return fmt.Errorf("XA branch timeout error xid:%s", c.txCtx.XID)
  312. }
  313. return nil
  314. }
  315. func (c *XAConn) Close() error {
  316. c.rollBacked = false
  317. if c.isConnKept && c.ShouldBeHeld() {
  318. return nil
  319. }
  320. c.cleanXABranchContext()
  321. if err := c.Conn.Close(); err != nil {
  322. return err
  323. }
  324. return nil
  325. }
  326. func (c *XAConn) CloseForce() error {
  327. if err := c.Conn.Close(); err != nil {
  328. return err
  329. }
  330. c.rollBacked = false
  331. c.cleanXABranchContext()
  332. c.releaseIfNecessary()
  333. return nil
  334. }
  335. func (c *XAConn) XaCommit(ctx context.Context, xaXid XAXid) error {
  336. err := c.xaResource.Commit(ctx, xaXid.String(), false)
  337. c.releaseIfNecessary()
  338. return err
  339. }
  340. func (c *XAConn) XaRollbackByBranchId(ctx context.Context, xaXid XAXid) error {
  341. return c.XaRollback(ctx, xaXid)
  342. }
  343. func (c *XAConn) XaRollback(ctx context.Context, xaXid XAXid) error {
  344. err := c.xaResource.Rollback(ctx, xaXid.String())
  345. c.releaseIfNecessary()
  346. return err
  347. }