View Javadoc
1   /*
2    * Copyright 2021 TiKV Project Authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   */
17  
18  package org.tikv.common.region;
19  
20  import com.google.protobuf.ByteString;
21  import java.io.Serializable;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Objects;
25  import java.util.Set;
26  import java.util.stream.Collectors;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  import org.tikv.common.TiConfiguration;
30  import org.tikv.common.codec.KeyUtils;
31  import org.tikv.common.exception.TiClientInternalException;
32  import org.tikv.common.key.Key;
33  import org.tikv.common.replica.ReplicaSelector;
34  import org.tikv.common.util.FastByteComparisons;
35  import org.tikv.common.util.KeyRangeUtils;
36  import org.tikv.kvproto.Kvrpcpb;
37  import org.tikv.kvproto.Kvrpcpb.IsolationLevel;
38  import org.tikv.kvproto.Metapb;
39  import org.tikv.kvproto.Metapb.Peer;
40  import org.tikv.kvproto.Metapb.Region;
41  
42  public class TiRegion implements Serializable {
43  
44    private static final Logger logger = LoggerFactory.getLogger(TiRegion.class);
45  
46    private final Region meta;
47    private final IsolationLevel isolationLevel;
48    private final Kvrpcpb.CommandPri commandPri;
49    private final TiConfiguration conf;
50    private final Peer leader;
51    private final ReplicaSelector replicaSelector;
52    private final List<Peer> replicaList;
53    private int replicaIdx;
54    private final List<Peer> peers;
55    private final List<TiStore> stores;
56  
57    public TiRegion(
58        TiConfiguration conf, Region meta, Peer leader, List<Peer> peers, List<TiStore> stores) {
59      this.conf = Objects.requireNonNull(conf, "conf is null");
60      this.meta = Objects.requireNonNull(meta, "meta is null");
61      this.isolationLevel = conf.getIsolationLevel();
62      this.commandPri = conf.getCommandPriority();
63      this.peers = peers;
64      this.stores = stores;
65      this.replicaSelector = conf.getReplicaSelector();
66      if (leader == null || leader.getId() == 0) {
67        if (meta.getPeersCount() == 0) {
68          throw new TiClientInternalException("Empty peer list for region " + meta.getId());
69        }
70        // region's first peer is leader.
71        this.leader = meta.getPeers(0);
72      } else {
73        this.leader = leader;
74      }
75  
76      // init replicaList
77      replicaList =
78          replicaSelector
79              .select(new org.tikv.common.replica.Region(meta, this.leader, peers, stores))
80              .stream()
81              .map(org.tikv.common.replica.Store::getPeer)
82              .collect(Collectors.toList());
83      replicaIdx = 0;
84    }
85  
86    public TiConfiguration getConf() {
87      return conf;
88    }
89  
90    public Peer getLeader() {
91      return leader;
92    }
93  
94    public List<Peer> getFollowerList() {
95      List<Peer> peers = new ArrayList<>();
96      for (Peer peer : getMeta().getPeersList()) {
97        if (!peer.equals(this.leader)) {
98          if (peer.getRole().equals(Metapb.PeerRole.Voter)) {
99            peers.add(peer);
100         }
101       }
102     }
103     return peers;
104   }
105 
106   public List<Peer> getLearnerList() {
107     List<Peer> peers = new ArrayList<>();
108     for (Peer peer : getMeta().getPeersList()) {
109       if (peer.getRole().equals(Metapb.PeerRole.Learner)) {
110         peers.add(peer);
111       }
112     }
113     return peers;
114   }
115 
116   public List<Peer> getPeersList() {
117     return getMeta().getPeersList();
118   }
119 
120   public Peer getCurrentReplica() {
121     return replicaList.get(replicaIdx);
122   }
123 
124   public Peer getNextReplica() {
125     replicaIdx = (replicaIdx + 1) % replicaList.size();
126     return getCurrentReplica();
127   }
128 
129   public void setReplicaIdx(int idx) {
130     replicaIdx = idx;
131   }
132 
133   public List<Peer> getReplicaList() {
134     return replicaList;
135   }
136 
137   private boolean isLeader(Peer peer) {
138     return getLeader().equals(peer);
139   }
140 
141   public long getId() {
142     return this.meta.getId();
143   }
144 
145   public ByteString getStartKey() {
146     return meta.getStartKey();
147   }
148 
149   public boolean contains(Key key) {
150     return KeyRangeUtils.makeRange(this.getStartKey(), this.getEndKey()).contains(key);
151   }
152 
153   public ByteString getEndKey() {
154     return meta.getEndKey();
155   }
156 
157   public Kvrpcpb.Context getLeaderContext() {
158     return getContext(this.leader, java.util.Collections.emptySet(), false);
159   }
160 
161   public Kvrpcpb.Context getReplicaContext(Set<Long> resolvedLocks, TiStoreType storeType) {
162     Peer currentPeer = getCurrentReplica();
163     boolean replicaRead = !isLeader(currentPeer) && TiStoreType.TiKV.equals(storeType);
164     return getContext(currentPeer, resolvedLocks, replicaRead);
165   }
166 
167   public Kvrpcpb.Context getReplicaContext(Peer currentPeer, Set<Long> resolvedLocks) {
168     return getContext(currentPeer, resolvedLocks, false);
169   }
170 
171   public Kvrpcpb.Context getReplicaContext(Peer currentPeer) {
172     return getContext(currentPeer, java.util.Collections.emptySet(), false);
173   }
174 
175   private Kvrpcpb.Context getContext(
176       Peer currentPeer, Set<Long> resolvedLocks, boolean replicaRead) {
177 
178     Kvrpcpb.Context.Builder builder = Kvrpcpb.Context.newBuilder();
179     builder
180         .setApiVersion(conf.getApiVersion().toPb())
181         .setIsolationLevel(this.isolationLevel)
182         .setPriority(this.commandPri)
183         .setRegionId(meta.getId())
184         .setPeer(currentPeer)
185         .setReplicaRead(replicaRead)
186         .setRegionEpoch(this.meta.getRegionEpoch());
187     builder.addAllResolvedLocks(resolvedLocks);
188     return builder.build();
189   }
190 
191   // getVerID returns the Region's RegionVerID.
192   public RegionVerID getVerID() {
193     return new RegionVerID(
194         meta.getId(), meta.getRegionEpoch().getConfVer(), meta.getRegionEpoch().getVersion());
195   }
196 
197   /**
198    * switches current peer to the one on specific store. It return false if no peer matches the
199    * storeID.
200    *
201    * @param leaderStoreID is leader peer id.
202    * @return null if no peers matches the store id.
203    */
204   public TiRegion switchPeer(long leaderStoreID) {
205     List<Peer> peers = meta.getPeersList();
206     for (Peer p : peers) {
207       if (p.getStoreId() == leaderStoreID) {
208         return new TiRegion(this.conf, this.meta, p, peers, this.stores);
209       }
210     }
211     return null;
212   }
213 
214   public boolean isMoreThan(ByteString key) {
215     return FastByteComparisons.compareTo(
216             meta.getStartKey().toByteArray(),
217             0,
218             meta.getStartKey().size(),
219             key.toByteArray(),
220             0,
221             key.size())
222         > 0;
223   }
224 
225   public boolean isLessThan(ByteString key) {
226     return FastByteComparisons.compareTo(
227             meta.getEndKey().toByteArray(),
228             0,
229             meta.getEndKey().size(),
230             key.toByteArray(),
231             0,
232             key.size())
233         <= 0;
234   }
235 
236   public boolean contains(ByteString key) {
237     return !isMoreThan(key) && !isLessThan(key);
238   }
239 
240   public boolean isValid() {
241     return leader != null && meta != null;
242   }
243 
244   public Metapb.RegionEpoch getRegionEpoch() {
245     return this.meta.getRegionEpoch();
246   }
247 
248   public Region getMeta() {
249     return meta;
250   }
251 
252   @Override
253   public boolean equals(final Object another) {
254     if (!(another instanceof TiRegion)) {
255       return false;
256     }
257     TiRegion anotherRegion = ((TiRegion) another);
258     return anotherRegion.meta.equals(this.meta)
259         && anotherRegion.leader.equals(this.leader)
260         && anotherRegion.commandPri.equals(this.commandPri)
261         && anotherRegion.isolationLevel.equals(this.isolationLevel);
262   }
263 
264   @Override
265   public int hashCode() {
266     return Objects.hash(meta, leader, isolationLevel, commandPri);
267   }
268 
269   @Override
270   public String toString() {
271     return String.format(
272         "{Region[%d] ConfVer[%d] Version[%d] Store[%d] KeyRange[%s]:[%s]}",
273         getId(),
274         getRegionEpoch().getConfVer(),
275         getRegionEpoch().getVersion(),
276         getLeader().getStoreId(),
277         KeyUtils.formatBytesUTF8(getStartKey()),
278         KeyUtils.formatBytesUTF8(getEndKey()));
279   }
280 
281   public class RegionVerID {
282 
283     final long id;
284     final long confVer;
285     final long ver;
286 
287     RegionVerID(long id, long confVer, long ver) {
288       this.id = id;
289       this.confVer = confVer;
290       this.ver = ver;
291     }
292 
293     public long getId() {
294       return id;
295     }
296 
297     public long getConfVer() {
298       return confVer;
299     }
300 
301     public long getVer() {
302       return ver;
303     }
304 
305     @Override
306     public boolean equals(Object other) {
307       if (this == other) {
308         return true;
309       }
310       if (!(other instanceof RegionVerID)) {
311         return false;
312       }
313 
314       RegionVerID that = (RegionVerID) other;
315       return id == that.id && confVer == that.confVer && ver == that.ver;
316     }
317 
318     @Override
319     public int hashCode() {
320       int hash = Long.hashCode(id);
321       hash = hash * 31 + Long.hashCode(confVer);
322       hash = hash * 31 + Long.hashCode(ver);
323       return hash;
324     }
325   }
326 }