@@ -10,6 +10,7 @@ import (
1010 "bytes"
1111 gosql "database/sql"
1212 "fmt"
13+ mysqldriver "github.com/go-sql-driver/mysql"
1314 "math"
1415 "strconv"
1516 "strings"
@@ -304,9 +305,23 @@ func (a *Applier) Run() {
304305 Subject : a .subject + "_dtrev" ,
305306 StateDir : a .stateDir ,
306307 }
307- var cfg2 = * a .mysqlContext
308+ // cfg2 will be a deepcopy of a.mysqlContext
309+ cfg2 , err := a .storeManager .GetConfig (a .subject )
310+ if err != nil {
311+ a .onError (common .TaskStateDead , errors .Wrap (err , "GetConfig" ))
312+ return
313+ }
308314 cfg2 .SrcConnectionConfig = a .mysqlContext .DestConnectionConfig
309315 cfg2 .DestConnectionConfig = a .mysqlContext .SrcConnectionConfig
316+ for _ , dbItem := range cfg2 .ReplicateDoDb {
317+ for _ , tbItem := range dbItem .Tables {
318+ if len (tbItem .ColumnMapFrom ) > 0 && len (tbItem .ColumnMapTo ) == 0 {
319+ a .onError (common .TaskStateDead , errors .Wrap (err , "GetConfig" ))
320+ return
321+ }
322+ tbItem .ColumnMapFrom , tbItem .ColumnMapTo = tbItem .ColumnMapTo , tbItem .ColumnMapFrom
323+ }
324+ }
310325
311326 if strings .ToLower (a .mysqlContext .TwoWaySyncGtid ) == "auto" {
312327 cfg2 .AutoGtid = true
@@ -318,7 +333,7 @@ func (a *Applier) Run() {
318333 cfg2 .TwoWaySync = false
319334 cfg2 .TwoWaySyncGtid = ""
320335
321- a .revExtractor , err = NewExtractor (execCtx2 , & cfg2 , a .logger , a .storeManager , a .waitCh , a .ctx )
336+ a .revExtractor , err = NewExtractor (execCtx2 , cfg2 , a .logger , a .storeManager , a .waitCh , a .ctx )
322337 if err != nil {
323338 a .onError (common .TaskStateDead , errors .Wrap (err , "reversed Extractor" ))
324339 return
@@ -422,7 +437,7 @@ func (a *Applier) doFullCopy() {
422437 return
423438 case copyRows := <- a .dumpEntryQueue :
424439 //time.Sleep(20 * time.Second) // #348 stub
425- if err = a .ApplyEventQueries (a . db , copyRows ); err != nil {
440+ if err = a .ApplyEventQueries (copyRows ); err != nil {
426441 return
427442 }
428443 atomic .AddInt64 (a .memory1 , - int64 (copyRows .Size ()))
@@ -825,7 +840,7 @@ func (a *Applier) ValidateGrants() error {
825840 return fmt .Errorf ("user has insufficient privileges for applier. Needed:ALTER, CREATE, DROP, INDEX, REFERENCES, INSERT, DELETE, UPDATE, SELECT, TRIGGER ON *.*" )
826841}
827842
828- func (a * Applier ) ApplyEventQueries (db * gosql. DB , entry * common.DumpEntry ) (err error ) {
843+ func (a * Applier ) ApplyEventQueries (entry * common.DumpEntry ) (err error ) {
829844 a .logger .Debug ("ApplyEventQueries" , "schema" , entry .TableSchema , "table" , entry .TableName ,
830845 "rows" , len (entry .ValuesX ))
831846
@@ -879,29 +894,18 @@ func (a *Applier) ApplyEventQueries(db *gosql.DB, entry *common.DumpEntry) (err
879894
880895 queries = append (queries , entry .SqlMode , entry .DbSQL )
881896 queries = append (queries , entry .TbSQL ... )
882- tx , err := db .BeginTx (a .ctx , & gosql.TxOptions {})
883- if err != nil {
884- return err
885- }
897+
898+ conn := a .dbs [0 ].Db
886899 nRows := int64 (len (entry .ValuesX ))
887- defer func () {
888- if err != nil {
889- return
890- }
891- err = tx .Commit ()
892- if err == nil {
893- atomic .AddInt64 (& a .TotalRowsReplayed , nRows )
894- }
895- }()
896- if _ , err := tx .ExecContext (a .ctx , querySetFKChecksOff ); err != nil {
900+ if _ , err := conn .ExecContext (a .ctx , querySetFKChecksOff ); err != nil {
897901 return err
898902 }
899903 execQuery := func (query string ) error {
900904 a .logger .Debug ("ApplyEventQueries. exec" , "query" , g .StrLim (query , 256 ))
901- _ , err := tx .ExecContext (a .ctx , query )
905+ _ , err := conn .ExecContext (a .ctx , query )
902906 if err != nil {
903907 queryStart := g .StrLim (query , 10 ) // avoid printing sensitive information
904- errCtx := errors .Wrapf (err , "tx.Exec. queryStart %v seq " , queryStart )
908+ errCtx := errors .Wrapf (err , "tx.Exec. queryStart %v" , queryStart )
905909 if ! sql .IgnoreError (err ) {
906910 a .logger .Error ("ApplyEventQueries. exec error" , "err" , errCtx )
907911 return errCtx
@@ -958,13 +962,22 @@ func (a *Applier) ApplyEventQueries(db *gosql.DB, entry *common.DumpEntry) (err
958962 // last rows or sql too large
959963
960964 if needInsert {
961- err := execQuery (buf .String ())
962- buf .Reset ()
963- if err != nil {
964- return err
965+ for iTry := 0 ; ; iTry ++ {
966+ err := execQuery (buf .String ())
967+ if errIsMysqlDeadlock (err ) && iTry < a .mysqlContext .RetryTxLimit {
968+ a .logger .Info ("found deadlock. will retry tx" , "schema" , entry .TableSchema , "table" , entry .Table ,
969+ "iTry" , iTry )
970+ time .Sleep (retryTxDelay )
971+ continue
972+ } else if err != nil {
973+ return err
974+ }
975+ break
965976 }
977+ buf .Reset ()
966978 }
967979 }
980+ atomic .AddInt64 (& a .TotalRowsReplayed , nRows )
968981
969982 return nil
970983}
@@ -1214,3 +1227,15 @@ func (a *Applier) updateDumpProgressLoop() {
12141227 time .Sleep (time .Duration (interval ) * time .Second )
12151228 }
12161229}
1230+ func errIsMysqlDeadlock (err error ) bool {
1231+ if err != nil {
1232+ merr , isME := err .(* mysqldriver.MySQLError )
1233+ if isME {
1234+ return merr .Number == sql .ErrLockDeadlock
1235+ } else {
1236+ return false
1237+ }
1238+ } else {
1239+ return false
1240+ }
1241+ }
0 commit comments