github.com/KinWaiYuen/client-go/v2@v2.5.4/internal/mockstore/mocktikv/session.go (about)

     1  // Copyright 2021 TiKV Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // NOTE: The code in this file is based on code from the
    16  // TiDB project, licensed under the Apache License v 2.0
    17  //
    18  // https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/mockstore/mocktikv/session.go
    19  //
    20  
    21  // Copyright 2021 PingCAP, Inc.
    22  //
    23  // Licensed under the Apache License, Version 2.0 (the "License");
    24  // you may not use this file except in compliance with the License.
    25  // You may obtain a copy of the License at
    26  //
    27  //     http://www.apache.org/licenses/LICENSE-2.0
    28  //
    29  // Unless required by applicable law or agreed to in writing, software
    30  // distributed under the License is distributed on an "AS IS" BASIS,
    31  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    32  // See the License for the specific language governing permissions and
    33  // limitations under the License.
    34  
    35  package mocktikv
    36  
    37  import (
    38  	"github.com/gogo/protobuf/proto"
    39  	"github.com/pingcap/kvproto/pkg/errorpb"
    40  	"github.com/pingcap/kvproto/pkg/kvrpcpb"
    41  	"github.com/pingcap/kvproto/pkg/metapb"
    42  )
    43  
    44  // Session stores session scope rpc data.
    45  type Session struct {
    46  	cluster   *Cluster
    47  	mvccStore MVCCStore
    48  
    49  	// storeID stores id for current request
    50  	storeID uint64
    51  	// startKey is used for handling normal request.
    52  	startKey []byte
    53  	endKey   []byte
    54  	// rawStartKey is used for handling coprocessor request.
    55  	rawStartKey []byte
    56  	rawEndKey   []byte
    57  	// isolationLevel is used for current request.
    58  	isolationLevel kvrpcpb.IsolationLevel
    59  	resolvedLocks  []uint64
    60  }
    61  
    62  // GetIsolationLevel returns the session's isolation level.
    63  func (s *Session) GetIsolationLevel() kvrpcpb.IsolationLevel {
    64  	return s.isolationLevel
    65  }
    66  
    67  // GetMVCCStore returns the mock mvcc store.
    68  func (s *Session) GetMVCCStore() MVCCStore {
    69  	return s.mvccStore
    70  }
    71  
    72  // GetRawStartKey returns the raw start key of the request.
    73  func (s *Session) GetRawStartKey() []byte {
    74  	return s.rawStartKey
    75  }
    76  
    77  // GetRawEndKey returns the raw end key of the request.
    78  func (s *Session) GetRawEndKey() []byte {
    79  	return s.rawEndKey
    80  }
    81  
    82  // GetResolvedLocks returns the resolved locks of the request.
    83  func (s *Session) GetResolvedLocks() []uint64 {
    84  	return s.resolvedLocks
    85  }
    86  
    87  // CheckRequestContext checks if the context matches the request status.
    88  func (s *Session) CheckRequestContext(ctx *kvrpcpb.Context) *errorpb.Error {
    89  	ctxPeer := ctx.GetPeer()
    90  	if ctxPeer != nil && ctxPeer.GetStoreId() != s.storeID {
    91  		return &errorpb.Error{
    92  			Message:       *proto.String("store not match"),
    93  			StoreNotMatch: &errorpb.StoreNotMatch{},
    94  		}
    95  	}
    96  	region, leaderID := s.cluster.GetRegion(ctx.GetRegionId())
    97  	// No region found.
    98  	if region == nil {
    99  		return &errorpb.Error{
   100  			Message: *proto.String("region not found"),
   101  			RegionNotFound: &errorpb.RegionNotFound{
   102  				RegionId: *proto.Uint64(ctx.GetRegionId()),
   103  			},
   104  		}
   105  	}
   106  	var storePeer, leaderPeer *metapb.Peer
   107  	for _, p := range region.Peers {
   108  		if p.GetStoreId() == s.storeID {
   109  			storePeer = p
   110  		}
   111  		if p.GetId() == leaderID {
   112  			leaderPeer = p
   113  		}
   114  	}
   115  	// The Store does not contain a Peer of the Region.
   116  	if storePeer == nil {
   117  		return &errorpb.Error{
   118  			Message: *proto.String("region not found"),
   119  			RegionNotFound: &errorpb.RegionNotFound{
   120  				RegionId: *proto.Uint64(ctx.GetRegionId()),
   121  			},
   122  		}
   123  	}
   124  	// No leader.
   125  	if leaderPeer == nil {
   126  		return &errorpb.Error{
   127  			Message: *proto.String("no leader"),
   128  			NotLeader: &errorpb.NotLeader{
   129  				RegionId: *proto.Uint64(ctx.GetRegionId()),
   130  			},
   131  		}
   132  	}
   133  	// The Peer on the Store is not leader. If it's tiflash store , we pass this check.
   134  	if storePeer.GetId() != leaderPeer.GetId() && !isTiFlashStore(s.cluster.GetStore(storePeer.GetStoreId())) {
   135  		return &errorpb.Error{
   136  			Message: *proto.String("not leader"),
   137  			NotLeader: &errorpb.NotLeader{
   138  				RegionId: *proto.Uint64(ctx.GetRegionId()),
   139  				Leader:   leaderPeer,
   140  			},
   141  		}
   142  	}
   143  	// Region epoch does not match.
   144  	if !proto.Equal(region.GetRegionEpoch(), ctx.GetRegionEpoch()) {
   145  		nextRegion, _ := s.cluster.GetRegionByKey(region.GetEndKey())
   146  		currentRegions := []*metapb.Region{region}
   147  		if nextRegion != nil {
   148  			currentRegions = append(currentRegions, nextRegion)
   149  		}
   150  		return &errorpb.Error{
   151  			Message: *proto.String("epoch not match"),
   152  			EpochNotMatch: &errorpb.EpochNotMatch{
   153  				CurrentRegions: currentRegions,
   154  			},
   155  		}
   156  	}
   157  	s.startKey, s.endKey = region.StartKey, region.EndKey
   158  	s.isolationLevel = ctx.IsolationLevel
   159  	s.resolvedLocks = ctx.ResolvedLocks
   160  	return nil
   161  }
   162  
   163  func (s *Session) checkRequestSize(size int) *errorpb.Error {
   164  	// TiKV has a limitation on raft log size.
   165  	// mocktikv has no raft inside, so we check the request's size instead.
   166  	if size >= requestMaxSize {
   167  		return &errorpb.Error{
   168  			RaftEntryTooLarge: &errorpb.RaftEntryTooLarge{},
   169  		}
   170  	}
   171  	return nil
   172  }
   173  
   174  func (s *Session) checkRequest(ctx *kvrpcpb.Context, size int) *errorpb.Error {
   175  	if err := s.CheckRequestContext(ctx); err != nil {
   176  		return err
   177  	}
   178  	return s.checkRequestSize(size)
   179  }
   180  
   181  func (s *Session) checkKeyInRegion(key []byte) bool {
   182  	return regionContains(s.startKey, s.endKey, NewMvccKey(key))
   183  }
   184  
   185  func isTiFlashStore(store *metapb.Store) bool {
   186  	for _, l := range store.GetLabels() {
   187  		if l.GetKey() == "engine" && l.GetValue() == "tiflash" {
   188  			return true
   189  		}
   190  	}
   191  	return false
   192  }