func (s *RegionRequestSender) sendKVReqToRegion(ctx *RPCContext, req *kvrpcpb.Request, timeout time.Duration) (resp *kvrpcpb.Response, retry bool, err error) { req.Context = ctx.KVCtx resp, err = s.client.SendKVReq(ctx.Addr, req, timeout) if err != nil { if e := s.onSendFail(ctx, err); e != nil { return nil, false, errors.Trace(e) } return nil, true, nil } return }
// sendKVReq sends req to tikv server. It will retry internally to find the right // region leader if i) fails to establish a connection to server or ii) server // returns `NotLeader`. func (s *tikvStore) SendKVReq(bo *Backoffer, req *pb.Request, regionID RegionVerID) (*pb.Response, error) { for { region := s.regionCache.GetRegionByVerID(regionID) if region == nil { // If the region is not found in cache, it must be out // of date and already be cleaned up. We can skip the // RPC by returning RegionError directly. return &pb.Response{ Type: req.GetType(), RegionError: &errorpb.Error{StaleEpoch: &errorpb.StaleEpoch{}}, }, nil } req.Context = region.GetContext() resp, err := s.client.SendKVReq(region.GetAddress(), req) if err != nil { s.regionCache.NextPeer(region.VerID()) err = bo.Backoff(boTiKVRPC, errors.Errorf("send tikv request error: %v, ctx: %s, try next peer later", err, req.Context)) if err != nil { return nil, errors.Trace(err) } continue } if regionErr := resp.GetRegionError(); regionErr != nil { // Retry if error is `NotLeader`. if notLeader := regionErr.GetNotLeader(); notLeader != nil { log.Warnf("tikv reports `NotLeader`: %s, ctx: %s, retry later", notLeader, req.Context) s.regionCache.UpdateLeader(region.VerID(), notLeader.GetLeader().GetId()) if notLeader.GetLeader() == nil { err = bo.Backoff(boRegionMiss, errors.Errorf("not leader: %v, ctx: %s", notLeader, req.Context)) if err != nil { return nil, errors.Trace(err) } } continue } // For other errors, we only drop cache here. // Because caller may need to re-split the request. log.Warnf("tikv reports region error: %s, ctx: %s", resp.GetRegionError(), req.Context) s.regionCache.DropRegion(region.VerID()) return resp, nil } if resp.GetType() != req.GetType() { return nil, errors.Trace(errMismatch(resp, req)) } return resp, nil } }