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  package org.tikv.common.region;
18  
19  import static org.tikv.common.codec.KeyUtils.formatBytesUTF8;
20  import static org.tikv.common.util.KeyRangeUtils.makeRange;
21  
22  import com.google.common.collect.RangeMap;
23  import com.google.common.collect.TreeRangeMap;
24  import com.google.protobuf.ByteString;
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  import org.tikv.common.key.Key;
32  import org.tikv.common.util.BackOffer;
33  
34  public class RegionCache {
35    private static final Logger logger = LoggerFactory.getLogger(RegionCache.class);
36  
37    private final Map<Long, TiRegion> regionCache;
38    private final Map<Long, TiStore> storeCache;
39    private final RangeMap<Key, Long> keyToRegionIdCache;
40  
41    public RegionCache() {
42      regionCache = new HashMap<>();
43      storeCache = new HashMap<>();
44  
45      keyToRegionIdCache = TreeRangeMap.create();
46    }
47  
48    public synchronized void invalidateAll() {
49      regionCache.clear();
50      storeCache.clear();
51      keyToRegionIdCache.clear();
52    }
53  
54    public synchronized TiRegion getRegionByKey(ByteString key, BackOffer backOffer) {
55      Long regionId;
56      if (key.isEmpty()) {
57        // if key is empty, it must be the start key.
58        regionId = keyToRegionIdCache.get(Key.toRawKey(key, true));
59      } else {
60        regionId = keyToRegionIdCache.get(Key.toRawKey(key));
61      }
62      if (logger.isDebugEnabled()) {
63        logger.debug(
64            String.format("getRegionByKey key[%s] -> ID[%s]", formatBytesUTF8(key), regionId));
65      }
66  
67      if (regionId == null) {
68        return null;
69      }
70      TiRegion region;
71      region = regionCache.get(regionId);
72      if (logger.isDebugEnabled()) {
73        logger.debug(String.format("getRegionByKey ID[%s] -> Region[%s]", regionId, region));
74      }
75      return region;
76    }
77  
78    public synchronized TiRegion putRegion(TiRegion region) {
79      if (logger.isDebugEnabled()) {
80        logger.debug("putRegion: " + region);
81      }
82      TiRegion oldRegion = regionCache.get(region.getId());
83      if (oldRegion != null) {
84        if (oldRegion.getMeta().equals(region.getMeta())) {
85          return oldRegion;
86        } else {
87          invalidateRegion(oldRegion);
88        }
89      }
90      regionCache.put(region.getId(), region);
91      keyToRegionIdCache.put(makeRange(region.getStartKey(), region.getEndKey()), region.getId());
92      return region;
93    }
94  
95    @Deprecated
96    public synchronized TiRegion getRegionById(long regionId) {
97      TiRegion region = regionCache.get(regionId);
98      if (logger.isDebugEnabled()) {
99        logger.debug(String.format("getRegionByKey ID[%s] -> Region[%s]", regionId, region));
100     }
101     return region;
102   }
103 
104   private synchronized TiRegion getRegionFromCache(long regionId) {
105     return regionCache.get(regionId);
106   }
107 
108   /** Removes region associated with regionId from regionCache. */
109   public synchronized void invalidateRegion(TiRegion region) {
110     try {
111       if (logger.isDebugEnabled()) {
112         logger.debug(String.format("invalidateRegion ID[%s]", region.getId()));
113       }
114       TiRegion oldRegion = regionCache.get(region.getId());
115       if (oldRegion != null && oldRegion == region) {
116         keyToRegionIdCache.remove(makeRange(region.getStartKey(), region.getEndKey()));
117         regionCache.remove(region.getId());
118       }
119     } catch (Exception ignore) {
120     }
121   }
122 
123   public synchronized void insertRegionToCache(TiRegion region) {
124     try {
125       TiRegion oldRegion = regionCache.get(region.getId());
126       if (oldRegion != null) {
127         keyToRegionIdCache.remove(makeRange(oldRegion.getStartKey(), oldRegion.getEndKey()));
128       }
129       regionCache.put(region.getId(), region);
130       keyToRegionIdCache.put(makeRange(region.getStartKey(), region.getEndKey()), region.getId());
131     } catch (Exception ignore) {
132     }
133   }
134 
135   public synchronized boolean updateRegion(TiRegion expected, TiRegion region) {
136     try {
137       if (logger.isDebugEnabled()) {
138         logger.debug(String.format("invalidateRegion ID[%s]", region.getId()));
139       }
140       TiRegion oldRegion = regionCache.get(region.getId());
141       if (!expected.getMeta().equals(oldRegion.getMeta())) {
142         return false;
143       } else {
144         if (oldRegion != null) {
145           keyToRegionIdCache.remove(makeRange(oldRegion.getStartKey(), oldRegion.getEndKey()));
146         }
147         regionCache.put(region.getId(), region);
148         keyToRegionIdCache.put(makeRange(region.getStartKey(), region.getEndKey()), region.getId());
149         return true;
150       }
151     } catch (Exception ignore) {
152       return false;
153     }
154   }
155 
156   public synchronized boolean updateStore(TiStore oldStore, TiStore newStore) {
157     if (!newStore.isValid()) {
158       return false;
159     }
160     if (oldStore == null) {
161       storeCache.put(newStore.getId(), newStore);
162       return true;
163     }
164     TiStore originStore = storeCache.get(oldStore.getId());
165     if (originStore.equals(oldStore)) {
166       storeCache.put(newStore.getId(), newStore);
167       oldStore.markInvalid();
168       return true;
169     }
170     return false;
171   }
172 
173   public synchronized void invalidateAllRegionForStore(TiStore store) {
174     TiStore oldStore = storeCache.get(store.getId());
175     if (oldStore != store) {
176       return;
177     }
178     List<TiRegion> regionToRemove = new ArrayList<>();
179     for (TiRegion r : regionCache.values()) {
180       if (r.getLeader().getStoreId() == store.getId()) {
181         if (logger.isDebugEnabled()) {
182           logger.debug(String.format("invalidateAllRegionForStore Region[%s]", r));
183         }
184         regionToRemove.add(r);
185       }
186     }
187 
188     logger.warn(String.format("invalid store [%d]", store.getId()));
189     // remove region
190     for (TiRegion r : regionToRemove) {
191       keyToRegionIdCache.remove(makeRange(r.getStartKey(), r.getEndKey()));
192       regionCache.remove(r.getId());
193     }
194   }
195 
196   public synchronized void invalidateStore(long storeId) {
197     TiStore store = storeCache.remove(storeId);
198     if (store != null) {
199       store.markInvalid();
200     }
201   }
202 
203   public synchronized TiStore getStoreById(long id) {
204     return storeCache.get(id);
205   }
206 
207   public synchronized boolean putStore(long id, TiStore store) {
208     TiStore oldStore = storeCache.get(id);
209     if (oldStore != null) {
210       if (oldStore.equals(store)) {
211         return false;
212       } else {
213         oldStore.markInvalid();
214       }
215     }
216     storeCache.put(id, store);
217     return true;
218   }
219 
220   public synchronized void clearAll() {
221     keyToRegionIdCache.clear();
222     regionCache.clear();
223   }
224 }