func (s *session) Retry() error { s.retrying = true nh := s.history.clone() // Debug infos. if len(nh.history) == 0 { s.debugInfos[retryEmptyHistoryList] = true } else { s.debugInfos[retryEmptyHistoryList] = false } defer func() { s.history.history = nh.history s.retrying = false }() if forUpdate := s.Value(forupdate.ForUpdateKey); forUpdate != nil { return errors.Errorf("can not retry select for update statement") } var err error retryCnt := 0 for { s.resetHistory() s.FinishTxn(true) success := true for _, sr := range nh.history { st := sr.st // Skip prepare statement if !needRetry(st) { continue } log.Warnf("Retry %s", st.OriginText()) switch st.(type) { case *stmts.ExecuteStmt: _, err = runStmt(s, st, sr.params...) default: _, err = runStmt(s, st) } if err != nil { if kv.IsRetryableError(err) { success = false break } log.Warnf("session:%v, err:%v", s, err) return errors.Trace(err) } } if success { err = s.FinishTxn(false) if !kv.IsRetryableError(err) { break } } retryCnt++ if (s.maxRetryCnt != unlimitedRetryCnt) && (retryCnt >= s.maxRetryCnt) { return errors.Trace(err) } kv.BackOff(retryCnt) } return err }
func (s *session) Retry() error { variable.GetSessionVars(s).RetryInfo.Retrying = true nh := s.history.clone() // Debug infos. if len(nh.history) == 0 { s.debugInfos[retryEmptyHistoryList] = true } else { s.debugInfos[retryEmptyHistoryList] = false } defer func() { s.history.history = nh.history variable.GetSessionVars(s).RetryInfo.Retrying = false }() if forUpdate := s.Value(forupdate.ForUpdateKey); forUpdate != nil { return errors.Errorf("can not retry select for update statement") } var err error retryCnt := 0 for { variable.GetSessionVars(s).RetryInfo.Attempts = retryCnt + 1 s.resetHistory() log.Info("RollbackTxn for retry txn.") err = s.RollbackTxn() if err != nil { // TODO: handle this error. log.Errorf("rollback txn failed, err:%v", errors.ErrorStack(err)) } success := true variable.GetSessionVars(s).RetryInfo.ResetOffset() for _, sr := range nh.history { st := sr.st log.Warnf("Retry %s", st.OriginText()) _, err = runStmt(s, st) if err != nil { if kv.IsRetryableError(err) { success = false break } log.Warnf("session:%v, err:%v", s, err) return errors.Trace(err) } } if success { err = s.CommitTxn() if !kv.IsRetryableError(err) { break } } retryCnt++ if (s.maxRetryCnt != unlimitedRetryCnt) && (retryCnt >= s.maxRetryCnt) { return errors.Trace(err) } kv.BackOff(retryCnt) } return err }
func (s *session) FinishTxn(rollback bool) error { // transaction has already been committed or rolled back if s.txn == nil { return nil } defer func() { s.txn = nil variable.GetSessionVars(s).SetStatusFlag(mysql.ServerStatusInTrans, false) }() if rollback { s.resetHistory() return s.txn.Rollback() } err := s.txn.Commit() if err != nil { if !s.retrying && kv.IsRetryableError(err) { err = s.Retry() } if err != nil { log.Warnf("txn:%s, %v", s.txn, err) return errors.Trace(err) } } s.resetHistory() return nil }
func newStoreWithRetry(uri string, maxRetries int) (kv.Storage, error) { pos := strings.Index(uri, "://") if pos == -1 { return nil, errors.Errorf("invalid uri format, must engine://schema") } name := strings.ToLower(uri[0:pos]) schema := uri[pos+3:] d, ok := stores[name] if !ok { return nil, errors.Errorf("invalid uri foramt, storage %s is not registered", name) } var err error var s kv.Storage for i := 1; i <= maxRetries; i++ { s, err = d.Open(schema) if err == nil || !kv.IsRetryableError(err) { break } sleepTime := time.Duration(uint64(retrySleepInterval) * uint64(i)) log.Warnf("Waiting store to get ready, sleep %v and try again...", sleepTime) time.Sleep(sleepTime) } return s, errors.Trace(err) }
func (s *testIsolationSuite) GetWithRetry(c *C, k []byte) readRecord { for { txn, err := s.store.Begin() c.Assert(err, IsNil) val, err := txn.Get(k) if err == nil { return readRecord{ startTS: txn.StartTS(), value: val, } } c.Assert(kv.IsRetryableError(err), IsTrue) } }
func (s *session) finishTxn(rollback bool) error { // transaction has already been committed or rolled back if s.txn == nil { return nil } defer func() { s.ClearValue(executor.DirtyDBKey) s.txn = nil variable.GetSessionVars(s).SetStatusFlag(mysql.ServerStatusInTrans, false) binloginfo.ClearBinlog(s) }() if rollback { s.resetHistory() s.cleanRetryInfo() return s.txn.Rollback() } if binloginfo.PumpClient != nil { prewriteValue := binloginfo.GetPrewriteValue(s, false) if prewriteValue != nil { prewriteData, err := prewriteValue.Marshal() if err != nil { return errors.Trace(err) } bin := &binlog.Binlog{ Tp: binlog.BinlogType_Prewrite, PrewriteValue: prewriteData, } s.txn.SetOption(kv.BinlogData, bin) } } err := s.txn.Commit() if err != nil { if !variable.GetSessionVars(s).RetryInfo.Retrying && kv.IsRetryableError(err) { err = s.Retry() } if err != nil { log.Warnf("txn:%s, %v", s.txn, err) return errors.Trace(err) } } s.resetHistory() s.cleanRetryInfo() return nil }
func (s *testIsolationSuite) SetWithRetry(c *C, k, v []byte) writeRecord { for { txn, err := s.store.Begin() c.Assert(err, IsNil) err = txn.Set(k, v) c.Assert(err, IsNil) err = txn.Commit() if err == nil { return writeRecord{ startTS: txn.StartTS(), commitTS: txn.(*tikvTxn).commitTS, } } c.Assert(kv.IsRetryableError(err), IsTrue) } }
func newStoreWithRetry(path string, maxRetries int) (kv.Storage, error) { url, err := url.Parse(path) if err != nil { return nil, errors.Trace(err) } name := strings.ToLower(url.Scheme) d, ok := stores[name] if !ok { return nil, errors.Errorf("invalid uri format, storage %s is not registered", name) } var s kv.Storage util.RunWithRetry(maxRetries, retryInterval, func() (bool, error) { s, err = d.Open(path) return kv.IsRetryableError(err), err }) return s, errors.Trace(err) }
func (s *session) Retry() error { nh := s.history.clone() defer func() { s.history.history = nh.history }() if forUpdate := s.Value(forupdate.ForUpdateKey); forUpdate != nil { return errors.Errorf("can not retry select for update statement") } var err error for { s.resetHistory() s.FinishTxn(true) success := true for _, sr := range nh.history { st := sr.st // Skip prepare statement if !needRetry(st) { continue } log.Warnf("Retry %s", st.OriginText()) _, err = runStmt(s, st) if err != nil { if terror.ErrorEqual(err, kv.ErrConditionNotMatch) { success = false break } log.Warnf("session:%v, err:%v", s, err) return errors.Trace(err) } } if success { err = s.FinishTxn(false) if !kv.IsRetryableError(err) { break } } } return err }
// If forceNew is true, GetTxn() must return a new transaction. // In this situation, if current transaction is still in progress, // there will be an implicit commit and create a new transaction. func (s *session) GetTxn(forceNew bool) (kv.Transaction, error) { var err error if s.txn == nil { s.resetHistory() s.txn, err = s.store.Begin() if err != nil { return nil, err } if !s.isAutocommit(s) { variable.GetSessionVars(s).SetStatusFlag(mysql.ServerStatusInTrans, true) } log.Infof("New txn:%s in session:%d", s.txn, s.sid) return s.txn, nil } if forceNew { err = s.txn.Commit() variable.GetSessionVars(s).SetStatusFlag(mysql.ServerStatusInTrans, false) if err != nil { if kv.IsRetryableError(err) { err = s.Retry() } if err != nil { return nil, errors.Trace(err) } } s.resetHistory() s.txn, err = s.store.Begin() if err != nil { return nil, err } if !s.isAutocommit(s) { variable.GetSessionVars(s).SetStatusFlag(mysql.ServerStatusInTrans, true) } log.Warnf("Force new txn:%s in session:%d", s.txn, s.sid) } return s.txn, nil }
func newStoreWithRetry(path string, maxRetries int) (kv.Storage, error) { url, err := url.Parse(path) if err != nil { return nil, errors.Trace(err) } name := strings.ToLower(url.Scheme) d, ok := stores[name] if !ok { return nil, errors.Errorf("invalid uri format, storage %s is not registered", name) } var s kv.Storage for i := 1; i <= maxRetries; i++ { s, err = d.Open(path) if err == nil || !kv.IsRetryableError(err) { break } sleepTime := time.Duration(uint64(retrySleepInterval) * uint64(i)) log.Warnf("Waiting store to get ready, sleep %v and try again...", sleepTime) time.Sleep(sleepTime) } return s, errors.Trace(err) }
func (s *session) finishTxn(rollback bool) error { // transaction has already been committed or rolled back if s.txn == nil { return nil } defer func() { s.ClearValue(executor.DirtyDBKey) s.txn = nil variable.GetSessionVars(s).SetStatusFlag(mysql.ServerStatusInTrans, false) // Update tps metrics if !variable.GetSessionVars(s).RetryInfo.Retrying { tpsMetrics.Add(1) } }() if rollback { s.resetHistory() s.cleanRetryInfo() return s.txn.Rollback() } err := s.txn.Commit() if err != nil { if !variable.GetSessionVars(s).RetryInfo.Retrying && kv.IsRetryableError(err) { err = s.Retry() } if err != nil { log.Warnf("txn:%s, %v", s.txn, err) return errors.Trace(err) } } s.resetHistory() s.cleanRetryInfo() return nil }
func (s *session) isRetryableError(err error) bool { return kv.IsRetryableError(err) || terror.ErrorEqual(err, domain.ErrInfoSchemaChanged) }