// Copyright 2021 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. package copr import ( "bytes" "github.com/pingcap/errors" "github.com/pingcap/tidb/kv" "github.com/tikv/client-go/v2/logutil" "github.com/tikv/client-go/v2/metrics" "github.com/tikv/client-go/v2/tikv" ) // RegionCache wraps tikv.RegionCache. type RegionCache struct { *tikv.RegionCache } // NewRegionCache returns a new RegionCache. func NewRegionCache(rc *tikv.RegionCache) *RegionCache { return &RegionCache{rc} } // SplitRegionRanges gets the split ranges from pd region. func (c *RegionCache) SplitRegionRanges(bo *Backoffer, keyRanges []kv.KeyRange) ([]kv.KeyRange, error) { ranges := NewKeyRanges(keyRanges) locations, err := c.SplitKeyRangesByLocations(bo, ranges) if err != nil { return nil, errors.Trace(err) } var ret []kv.KeyRange for _, loc := range locations { for i := 0; i < loc.Ranges.Len(); i++ { ret = append(ret, loc.Ranges.At(i)) } } return ret, nil } // LocationKeyRanges wrapps a real Location in PD and its logical ranges info. type LocationKeyRanges struct { // Location is the real location in PD. Location *tikv.KeyLocation // Ranges is the logic ranges the current Location contains. Ranges *KeyRanges } // SplitKeyRangesByLocations splits the KeyRanges by logical info in the cache. func (c *RegionCache) SplitKeyRangesByLocations(bo *Backoffer, ranges *KeyRanges) ([]*LocationKeyRanges, error) { res := make([]*LocationKeyRanges, 0) for ranges.Len() > 0 { loc, err := c.LocateKey(bo.TiKVBackoffer(), ranges.At(0).StartKey) if err != nil { return res, errors.Trace(err) } // Iterate to the first range that is not complete in the region. var i int for ; i < ranges.Len(); i++ { r := ranges.At(i) if !(loc.Contains(r.EndKey) || bytes.Equal(loc.EndKey, r.EndKey)) { break } } // All rest ranges belong to the same region. if i == ranges.Len() { res = append(res, &LocationKeyRanges{Location: loc, Ranges: ranges}) break } r := ranges.At(i) if loc.Contains(r.StartKey) { // Part of r is not in the region. We need to split it. taskRanges := ranges.Slice(0, i) taskRanges.last = &kv.KeyRange{ StartKey: r.StartKey, EndKey: loc.EndKey, } res = append(res, &LocationKeyRanges{Location: loc, Ranges: taskRanges}) ranges = ranges.Slice(i+1, ranges.Len()) ranges.first = &kv.KeyRange{ StartKey: loc.EndKey, EndKey: r.EndKey, } } else { // rs[i] is not in the region. taskRanges := ranges.Slice(0, i) res = append(res, &LocationKeyRanges{Location: loc, Ranges: taskRanges}) ranges = ranges.Slice(i, ranges.Len()) } } return res, nil } // OnSendFailForBatchRegions handles send request fail logic. func (c *RegionCache) OnSendFailForBatchRegions(bo *Backoffer, store *tikv.Store, regionInfos []RegionInfo, scheduleReload bool, err error) { metrics.RegionCacheCounterWithSendFail.Add(float64(len(regionInfos))) if !store.IsTiFlash() { logutil.Logger(bo.GetCtx()).Info("Should not reach here, OnSendFailForBatchRegions only support TiFlash") return } for _, ri := range regionInfos { if ri.Meta == nil { continue } c.OnSendFailForTiFlash(bo.TiKVBackoffer(), store, ri.Region, ri.Meta, scheduleReload, err) } }