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.

undo.go 14 kB

3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  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 base
  18. import (
  19. "context"
  20. "database/sql"
  21. "database/sql/driver"
  22. "encoding/json"
  23. "strconv"
  24. "strings"
  25. "github.com/arana-db/parser/mysql"
  26. "github.com/pkg/errors"
  27. "github.com/seata/seata-go/pkg/constant"
  28. "github.com/seata/seata-go/pkg/datasource/sql/datasource"
  29. "github.com/seata/seata-go/pkg/datasource/sql/types"
  30. "github.com/seata/seata-go/pkg/datasource/sql/undo"
  31. "github.com/seata/seata-go/pkg/datasource/sql/undo/factor"
  32. "github.com/seata/seata-go/pkg/util/log"
  33. )
  34. // checkUndoLogTableExistSql check undo log if exist
  35. var (
  36. ErrorDeleteUndoLogParamsFault = errors.New("xid or branch_id can't nil")
  37. )
  38. var (
  39. checkUndoLogTableExistSql = "SELECT 1 FROM " + constant.UndoLogTableName + " LIMIT 1"
  40. insertUndoLogSql = "INSERT INTO " + constant.UndoLogTableName + "(branch_id,xid,context,rollback_info,log_status,log_created,log_modified) VALUES (?, ?, ?, ?, ?, now(6), now(6))"
  41. selectUndoLogSql = "SELECT `branch_id`,`xid`,`context`,`rollback_info`,`log_status` FROM " + constant.UndoLogTableName + " WHERE " + constant.UndoLogBranchXid + " = ? AND " + constant.UndoLogXid + " = ? FOR UPDATE" // todo 替换成常量吧,不用使用变量来表示字段名
  42. )
  43. const (
  44. PairSplit = "&"
  45. KvSplit = "="
  46. CompressorTypeKey = "compressorTypeKey"
  47. SerializerKey = "serializerKey"
  48. // CheckUndoLogTableExistSql check undo log if exist
  49. CheckUndoLogTableExistSql = "SELECT 1 FROM " + constant.UndoLogTableName + " LIMIT 1"
  50. // DeleteUndoLogSql delete undo log
  51. DeleteUndoLogSql = constant.DeleteFrom + constant.UndoLogTableName + " WHERE " + constant.UndoLogBranchXid + " = ? AND " + constant.UndoLogXid + " = ?"
  52. )
  53. // undo log status
  54. const (
  55. // UndoLogStatusNormal This state can be properly rolled back by services
  56. UndoLogStatusNormal = iota
  57. // UndoLogStatusGlobalFinished This state prevents the branch transaction from inserting undo_log after the global transaction is rolled back.
  58. UndoLogStatusGlobalFinished
  59. )
  60. // BaseUndoLogManager
  61. type BaseUndoLogManager struct{}
  62. func NewBaseUndoLogManager() *BaseUndoLogManager {
  63. return &BaseUndoLogManager{}
  64. }
  65. // Init
  66. func (m *BaseUndoLogManager) Init() {
  67. }
  68. // InsertUndoLog
  69. func (m *BaseUndoLogManager) InsertUndoLog(record undo.UndologRecord, conn driver.Conn) error {
  70. stmt, err := conn.Prepare(insertUndoLogSql)
  71. if err != nil {
  72. return err
  73. }
  74. _, err = stmt.Exec([]driver.Value{record.BranchID, record.XID, record.Context, record.RollbackInfo, int64(record.LogStatus)})
  75. if err != nil {
  76. return err
  77. }
  78. return nil
  79. }
  80. func (m *BaseUndoLogManager) InsertUndoLogWithSqlConn(ctx context.Context, record undo.UndologRecord, conn *sql.Conn) error {
  81. stmt, err := conn.PrepareContext(ctx, insertUndoLogSql)
  82. if err != nil {
  83. return err
  84. }
  85. _, err = stmt.Exec(record.BranchID, record.XID, record.Context, record.RollbackInfo, int64(record.LogStatus))
  86. if err != nil {
  87. return err
  88. }
  89. return nil
  90. }
  91. // DeleteUndoLog exec delete single undo log operate
  92. func (m *BaseUndoLogManager) DeleteUndoLog(ctx context.Context, xid string, branchID int64, conn *sql.Conn) error {
  93. stmt, err := conn.PrepareContext(ctx, constant.DeleteUndoLogSql)
  94. if err != nil {
  95. log.Errorf("[DeleteUndoLog] prepare sql fail, err: %v", err)
  96. return err
  97. }
  98. if _, err = stmt.Exec(branchID, xid); err != nil {
  99. log.Errorf("[DeleteUndoLog] exec delete undo log fail, err: %v", err)
  100. return err
  101. }
  102. return nil
  103. }
  104. // BatchDeleteUndoLog exec delete undo log operate
  105. func (m *BaseUndoLogManager) BatchDeleteUndoLog(xid []string, branchID []int64, conn *sql.Conn) error {
  106. // build delete undo log sql
  107. batchDeleteSql, err := m.getBatchDeleteUndoLogSql(xid, branchID)
  108. if err != nil {
  109. log.Errorf("get undo sql log fail, err: %v", err)
  110. return err
  111. }
  112. ctx := context.Background()
  113. // prepare deal sql
  114. stmt, err := conn.PrepareContext(ctx, batchDeleteSql)
  115. if err != nil {
  116. log.Errorf("prepare sql fail, err: %v", err)
  117. return err
  118. }
  119. branchIDStr, err := Int64Slice2Str(branchID, ",")
  120. if err != nil {
  121. log.Errorf("slice to string transfer fail, err: %v", err)
  122. return err
  123. }
  124. // exec sql stmt
  125. if _, err = stmt.ExecContext(ctx, branchIDStr, strings.Join(xid, ",")); err != nil {
  126. log.Errorf("exec delete undo log fail, err: %v", err)
  127. return err
  128. }
  129. return nil
  130. }
  131. // FlushUndoLog flush undo log
  132. func (m *BaseUndoLogManager) FlushUndoLog(tranCtx *types.TransactionContext, conn driver.Conn) error {
  133. if tranCtx.RoundImages.IsEmpty() {
  134. return nil
  135. }
  136. sqlUndoLogs := make([]undo.SQLUndoLog, 0)
  137. beforeImages := tranCtx.RoundImages.BeofreImages()
  138. afterImages := tranCtx.RoundImages.AfterImages()
  139. if beforeImages.IsEmptyImage() && afterImages.IsEmptyImage() {
  140. return nil
  141. }
  142. for i := 0; i < len(beforeImages); i++ {
  143. var (
  144. tableName string
  145. sqlType types.SQLType
  146. )
  147. if beforeImages[i] != nil {
  148. tableName = beforeImages[i].TableName
  149. sqlType = beforeImages[i].SQLType
  150. } else if afterImages[i] != nil {
  151. tableName = afterImages[i].TableName
  152. sqlType = afterImages[i].SQLType
  153. } else {
  154. continue
  155. }
  156. undoLog := undo.SQLUndoLog{
  157. SQLType: sqlType,
  158. TableName: tableName,
  159. BeforeImage: beforeImages[i],
  160. AfterImage: afterImages[i],
  161. }
  162. sqlUndoLogs = append(sqlUndoLogs, undoLog)
  163. }
  164. branchUndoLog := undo.BranchUndoLog{
  165. Xid: tranCtx.XID,
  166. BranchID: tranCtx.BranchID,
  167. Logs: sqlUndoLogs,
  168. }
  169. // use defalut encode
  170. rollbackInfo, err := json.Marshal(branchUndoLog)
  171. if err != nil {
  172. return err
  173. }
  174. parseContext := make(map[string]string, 0)
  175. parseContext[SerializerKey] = "jackson"
  176. parseContext[CompressorTypeKey] = "NONE"
  177. undoLogContent, err := json.Marshal(parseContext)
  178. if err != nil {
  179. return err
  180. }
  181. return m.InsertUndoLog(undo.UndologRecord{
  182. BranchID: tranCtx.BranchID,
  183. XID: tranCtx.XID,
  184. Context: undoLogContent,
  185. RollbackInfo: rollbackInfo,
  186. LogStatus: undo.UndoLogStatueNormnal,
  187. }, conn)
  188. }
  189. // RunUndo undo sql
  190. func (m *BaseUndoLogManager) RunUndo(ctx context.Context, xid string, branchID int64, conn *sql.DB, dbName string) error {
  191. return nil
  192. }
  193. // Undo undo sql
  194. func (m *BaseUndoLogManager) Undo(ctx context.Context, dbType types.DBType, xid string, branchID int64, db *sql.DB, dbName string) (err error) {
  195. conn, err := db.Conn(ctx)
  196. if err != nil {
  197. return err
  198. }
  199. tx, err := conn.BeginTx(ctx, &sql.TxOptions{})
  200. if err != nil {
  201. return err
  202. }
  203. defer func() {
  204. if err != nil {
  205. if err = tx.Rollback(); err != nil {
  206. log.Errorf("rollback fail, xid: %s, branchID:%s err:%v", xid, branchID, err)
  207. return
  208. }
  209. }
  210. }()
  211. stmt, err := conn.PrepareContext(ctx, selectUndoLogSql)
  212. if err != nil {
  213. log.Errorf("prepare sql fail, err: %v", err)
  214. return err
  215. }
  216. defer func() {
  217. if err = stmt.Close(); err != nil {
  218. log.Errorf("stmt close fail, xid: %s, branchID:%s err:%v", xid, branchID, err)
  219. return
  220. }
  221. }()
  222. rows, err := stmt.Query(branchID, xid)
  223. if err != nil {
  224. log.Errorf("query sql fail, err: %v", err)
  225. return err
  226. }
  227. defer func() {
  228. if err = rows.Close(); err != nil {
  229. log.Errorf("rows close fail, xid: %s, branchID:%s err:%v", xid, branchID, err)
  230. return
  231. }
  232. }()
  233. var undoLogRecords []undo.UndologRecord
  234. for rows.Next() {
  235. var record undo.UndologRecord
  236. err = rows.Scan(&record.BranchID, &record.XID, &record.Context, &record.RollbackInfo, &record.LogStatus)
  237. if err != nil {
  238. return err
  239. }
  240. undoLogRecords = append(undoLogRecords, record)
  241. }
  242. var exists bool
  243. for _, record := range undoLogRecords {
  244. exists = true
  245. if !record.CanUndo() {
  246. log.Infof("xid %v branch %v, ignore %v undo_log", record.XID, record.BranchID, record.LogStatus)
  247. return nil
  248. }
  249. // todo use serializer and decode
  250. var branchUndoLog undo.BranchUndoLog
  251. if err = json.Unmarshal(record.RollbackInfo, &branchUndoLog); err != nil {
  252. return err
  253. }
  254. sqlUndoLogs := branchUndoLog.Logs
  255. if len(sqlUndoLogs) == 0 {
  256. return nil
  257. }
  258. branchUndoLog.Reverse()
  259. for _, undoLog := range sqlUndoLogs {
  260. tableMeta, err := datasource.GetTableCache(types.DBTypeMySQL).GetTableMeta(ctx, dbName, undoLog.TableName)
  261. if err != nil {
  262. log.Errorf("get table meta fail, err: %v", err)
  263. return err
  264. }
  265. undoLog.SetTableMeta(*tableMeta)
  266. undoExecutor, err := factor.GetUndoExecutor(dbType, undoLog)
  267. if err != nil {
  268. log.Errorf("get undo executor, err: %v", err)
  269. return err
  270. }
  271. if err = undoExecutor.ExecuteOn(ctx, dbType, conn); err != nil {
  272. log.Errorf("execute on fail, err: %v", err)
  273. return err
  274. }
  275. }
  276. }
  277. if exists {
  278. if err = m.DeleteUndoLog(ctx, xid, branchID, conn); err != nil {
  279. log.Errorf("[Undo] delete undo fail, err: %v", err)
  280. return err
  281. }
  282. log.Infof("xid %v branch %v, undo_log deleted with %v", xid, branchID, undo.UndoLogStatueGlobalFinished)
  283. } else {
  284. if err = m.insertUndoLogWithGlobalFinished(ctx, xid, uint64(branchID), conn); err != nil {
  285. log.Errorf("[Undo] insert undo with global finished fail, err: %v", err)
  286. return err
  287. }
  288. log.Infof("xid %v branch %v, undo_log added with %v", xid, branchID, undo.UndoLogStatueGlobalFinished)
  289. }
  290. if err = tx.Commit(); err != nil {
  291. log.Errorf("[Undo] execute on fail, err: %v", err)
  292. return nil
  293. }
  294. return nil
  295. }
  296. func (m *BaseUndoLogManager) insertUndoLogWithGlobalFinished(ctx context.Context, xid string, branchID uint64, conn *sql.Conn) error {
  297. // todo use config to replace
  298. parseContext := make(map[string]string, 0)
  299. parseContext[SerializerKey] = "jackson"
  300. parseContext[CompressorTypeKey] = "NONE"
  301. undoLogContent, err := json.Marshal(parseContext)
  302. if err != nil {
  303. return err
  304. }
  305. record := undo.UndologRecord{
  306. BranchID: branchID,
  307. XID: xid,
  308. RollbackInfo: []byte("{}"),
  309. LogStatus: UndoLogStatusGlobalFinished,
  310. Context: undoLogContent,
  311. }
  312. err = m.InsertUndoLogWithSqlConn(ctx, record, conn)
  313. if err != nil {
  314. log.Errorf("insert undo log fail, err: %v", err)
  315. return err
  316. }
  317. return nil
  318. }
  319. // DBType
  320. func (m *BaseUndoLogManager) DBType() types.DBType {
  321. panic("implement me")
  322. }
  323. // HasUndoLogTable check undo log table if exist
  324. func (m *BaseUndoLogManager) HasUndoLogTable(ctx context.Context, conn *sql.Conn) (res bool, err error) {
  325. if _, err = conn.QueryContext(ctx, checkUndoLogTableExistSql); err != nil {
  326. // 1146 mysql table not exist fault code
  327. if e, ok := err.(*mysql.SQLError); ok && e.Code == mysql.ErrNoSuchTable {
  328. return false, nil
  329. }
  330. log.Errorf("[HasUndoLogTable] query sql fail, err: %v", err)
  331. return
  332. }
  333. return true, nil
  334. }
  335. // getBatchDeleteUndoLogSql build batch delete undo log
  336. func (m *BaseUndoLogManager) getBatchDeleteUndoLogSql(xid []string, branchID []int64) (string, error) {
  337. if len(xid) == 0 || len(branchID) == 0 {
  338. return "", ErrorDeleteUndoLogParamsFault
  339. }
  340. var undoLogDeleteSql strings.Builder
  341. undoLogDeleteSql.WriteString(constant.DeleteFrom)
  342. undoLogDeleteSql.WriteString(constant.UndoLogTableName)
  343. undoLogDeleteSql.WriteString(" WHERE ")
  344. undoLogDeleteSql.WriteString(constant.UndoLogBranchXid)
  345. undoLogDeleteSql.WriteString(" IN ")
  346. m.appendInParam(len(branchID), &undoLogDeleteSql)
  347. undoLogDeleteSql.WriteString(" AND ")
  348. undoLogDeleteSql.WriteString(constant.UndoLogXid)
  349. undoLogDeleteSql.WriteString(" IN ")
  350. m.appendInParam(len(xid), &undoLogDeleteSql)
  351. return undoLogDeleteSql.String(), nil
  352. }
  353. // appendInParam build in param
  354. func (m *BaseUndoLogManager) appendInParam(size int, str *strings.Builder) {
  355. if size <= 0 {
  356. return
  357. }
  358. str.WriteString(" (")
  359. for i := 0; i < size; i++ {
  360. str.WriteString("?")
  361. if i < size-1 {
  362. str.WriteString(",")
  363. }
  364. }
  365. str.WriteString(") ")
  366. }
  367. // Int64Slice2Str
  368. func Int64Slice2Str(values interface{}, sep string) (string, error) {
  369. v, ok := values.([]int64)
  370. if !ok {
  371. return "", errors.New("param type is fault")
  372. }
  373. var valuesText []string
  374. for i := range v {
  375. text := strconv.FormatInt(v[i], 10)
  376. valuesText = append(valuesText, text)
  377. }
  378. return strings.Join(valuesText, sep), nil
  379. }
  380. // canUndo check if it can undo
  381. func (m *BaseUndoLogManager) canUndo(state int32) bool {
  382. return state == UndoLogStatusNormal
  383. }
  384. // parseContext parse undo context
  385. func (m *BaseUndoLogManager) parseContext(str string) map[string]string {
  386. return m.DecodeMap(str)
  387. }
  388. // DecodeMap Decode undo log context string to map
  389. func (m *BaseUndoLogManager) DecodeMap(str string) map[string]string {
  390. res := make(map[string]string)
  391. if str == "" {
  392. return nil
  393. }
  394. strSlice := strings.Split(str, PairSplit)
  395. if len(strSlice) == 0 {
  396. return nil
  397. }
  398. for key, _ := range strSlice {
  399. kv := strings.Split(strSlice[key], KvSplit)
  400. if len(kv) != 2 {
  401. continue
  402. }
  403. res[kv[0]] = kv[1]
  404. }
  405. return res
  406. }
  407. // getRollbackInfo parser rollback info
  408. func (m *BaseUndoLogManager) getRollbackInfo(rollbackInfo []byte, undoContext map[string]string) []byte {
  409. // Todo use compressor
  410. // get compress type
  411. /*compressorType, ok := undoContext[constant.CompressorTypeKey]
  412. if ok {
  413. }*/
  414. return rollbackInfo
  415. }
  416. // getSerializer get serializer from undo context
  417. func (m *BaseUndoLogManager) getSerializer(undoLogContext map[string]string) (serializer string) {
  418. if undoLogContext == nil {
  419. return
  420. }
  421. serializer, _ = undoLogContext[SerializerKey]
  422. return
  423. }