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 15 kB

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