1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.client;
20
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.Set;
24 import java.util.concurrent.ConcurrentMap;
25 import java.util.concurrent.ConcurrentNavigableMap;
26 import java.util.concurrent.CopyOnWriteArraySet;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.hbase.classification.InterfaceAudience;
30 import org.apache.hadoop.hbase.HConstants;
31 import org.apache.hadoop.hbase.HRegionInfo;
32 import org.apache.hadoop.hbase.HRegionLocation;
33 import org.apache.hadoop.hbase.KeyValue;
34 import org.apache.hadoop.hbase.KeyValue.KVComparator;
35 import org.apache.hadoop.hbase.RegionLocations;
36 import org.apache.hadoop.hbase.ServerName;
37 import org.apache.hadoop.hbase.TableName;
38 import org.apache.hadoop.hbase.types.CopyOnWriteArrayMap;
39 import org.apache.hadoop.hbase.util.Bytes;
40
41
42
43
44 @InterfaceAudience.Private
45 public class MetaCache {
46
47 private static final Log LOG = LogFactory.getLog(MetaCache.class);
48
49
50
51
52 private final ConcurrentMap<TableName, ConcurrentNavigableMap<byte[], RegionLocations>>
53 cachedRegionLocations =
54 new CopyOnWriteArrayMap<>();
55
56
57
58
59
60
61 private final Set<ServerName> cachedServers = new CopyOnWriteArraySet<>();
62
63 private final MetricsConnection metrics;
64
65 public MetaCache(MetricsConnection metrics) {
66 this.metrics = metrics;
67 }
68
69
70
71
72
73
74
75 public RegionLocations getCachedLocation(final TableName tableName, final byte [] row) {
76 ConcurrentNavigableMap<byte[], RegionLocations> tableLocations =
77 getTableLocations(tableName);
78
79 Entry<byte[], RegionLocations> e = tableLocations.floorEntry(row);
80 if (e == null) {
81 if (metrics!= null) metrics.incrMetaCacheMiss();
82 return null;
83 }
84 RegionLocations possibleRegion = e.getValue();
85
86
87
88
89
90
91 byte[] endKey = possibleRegion.getRegionLocation().getRegionInfo().getEndKey();
92 if (Bytes.equals(endKey, HConstants.EMPTY_END_ROW) ||
93 getRowComparator(tableName).compareRows(
94 endKey, 0, endKey.length, row, 0, row.length) > 0) {
95 if (metrics != null) metrics.incrMetaCacheHit();
96 return possibleRegion;
97 }
98
99
100 if (metrics != null) metrics.incrMetaCacheMiss();
101 return null;
102 }
103
104 private KVComparator getRowComparator(TableName tableName) {
105 return TableName.META_TABLE_NAME.equals(tableName) ? KeyValue.META_COMPARATOR
106 : KeyValue.COMPARATOR;
107 }
108
109
110
111
112
113
114 public void cacheLocation(final TableName tableName, final ServerName source,
115 final HRegionLocation location) {
116 assert source != null;
117 byte [] startKey = location.getRegionInfo().getStartKey();
118 ConcurrentMap<byte[], RegionLocations> tableLocations = getTableLocations(tableName);
119 RegionLocations locations = new RegionLocations(new HRegionLocation[] {location}) ;
120 RegionLocations oldLocations = tableLocations.putIfAbsent(startKey, locations);
121 boolean isNewCacheEntry = (oldLocations == null);
122 if (isNewCacheEntry) {
123 if (LOG.isTraceEnabled()) {
124 LOG.trace("Cached location: " + location);
125 }
126 addToCachedServers(locations);
127 return;
128 }
129
130
131 HRegionLocation oldLocation = oldLocations.getRegionLocation(
132 location.getRegionInfo().getReplicaId());
133 boolean force = oldLocation != null && oldLocation.getServerName() != null
134 && oldLocation.getServerName().equals(source);
135
136
137
138
139
140
141 RegionLocations updatedLocations = oldLocations.updateLocation(location, false, force);
142 if (oldLocations != updatedLocations) {
143 boolean replaced = tableLocations.replace(startKey, oldLocations, updatedLocations);
144 if (replaced && LOG.isTraceEnabled()) {
145 LOG.trace("Changed cached location to: " + location);
146 }
147 addToCachedServers(updatedLocations);
148 }
149 }
150
151
152
153
154
155
156 public void cacheLocation(final TableName tableName, final RegionLocations locations) {
157 byte [] startKey = locations.getRegionLocation().getRegionInfo().getStartKey();
158 ConcurrentMap<byte[], RegionLocations> tableLocations = getTableLocations(tableName);
159 RegionLocations oldLocation = tableLocations.putIfAbsent(startKey, locations);
160 boolean isNewCacheEntry = (oldLocation == null);
161 if (isNewCacheEntry) {
162 if (LOG.isTraceEnabled()) {
163 LOG.trace("Cached location: " + locations);
164 }
165 addToCachedServers(locations);
166 return;
167 }
168
169
170
171
172 RegionLocations mergedLocation = oldLocation.mergeLocations(locations);
173 boolean replaced = tableLocations.replace(startKey, oldLocation, mergedLocation);
174 if (replaced && LOG.isTraceEnabled()) {
175 LOG.trace("Merged cached locations: " + mergedLocation);
176 }
177 addToCachedServers(locations);
178 }
179
180 private void addToCachedServers(RegionLocations locations) {
181 for (HRegionLocation loc : locations.getRegionLocations()) {
182 if (loc != null) {
183 cachedServers.add(loc.getServerName());
184 }
185 }
186 }
187
188
189
190
191
192 private ConcurrentNavigableMap<byte[], RegionLocations>
193 getTableLocations(final TableName tableName) {
194
195 ConcurrentNavigableMap<byte[], RegionLocations> result;
196 result = this.cachedRegionLocations.get(tableName);
197
198 if (result == null) {
199 result = new CopyOnWriteArrayMap<>(Bytes.BYTES_COMPARATOR);
200 ConcurrentNavigableMap<byte[], RegionLocations> old =
201 this.cachedRegionLocations.putIfAbsent(tableName, result);
202 if (old != null) {
203 return old;
204 }
205 }
206 return result;
207 }
208
209
210
211
212
213
214
215 public boolean isRegionCached(TableName tableName, final byte[] row) {
216 RegionLocations location = getCachedLocation(tableName, row);
217 return location != null;
218 }
219
220
221
222
223
224 public int getNumberOfCachedRegionLocations(final TableName tableName) {
225 Map<byte[], RegionLocations> tableLocs = this.cachedRegionLocations.get(tableName);
226 if (tableLocs == null) {
227 return 0;
228 }
229 int numRegions = 0;
230 for (RegionLocations tableLoc : tableLocs.values()) {
231 numRegions += tableLoc.numNonNullElements();
232 }
233 return numRegions;
234 }
235
236
237
238
239 public void clearCache() {
240 this.cachedRegionLocations.clear();
241 this.cachedServers.clear();
242 }
243
244
245
246
247 public void clearCache(final ServerName serverName) {
248 if (!this.cachedServers.contains(serverName)) {
249 return;
250 }
251
252 boolean deletedSomething = false;
253 synchronized (this.cachedServers) {
254
255
256
257
258 if (!this.cachedServers.contains(serverName)) {
259 return;
260 }
261 for (ConcurrentMap<byte[], RegionLocations> tableLocations : cachedRegionLocations.values()){
262 for (Entry<byte[], RegionLocations> e : tableLocations.entrySet()) {
263 RegionLocations regionLocations = e.getValue();
264 if (regionLocations != null) {
265 RegionLocations updatedLocations = regionLocations.removeByServer(serverName);
266 if (updatedLocations != regionLocations) {
267 if (updatedLocations.isEmpty()) {
268 deletedSomething |= tableLocations.remove(e.getKey(), regionLocations);
269 } else {
270 deletedSomething |= tableLocations.replace(e.getKey(), regionLocations, updatedLocations);
271 }
272 }
273 }
274 }
275 }
276 this.cachedServers.remove(serverName);
277 }
278 if (deletedSomething) {
279 if (metrics != null) {
280 metrics.incrMetaCacheNumClearServer();
281 }
282 if (LOG.isTraceEnabled()) {
283 LOG.trace("Removed all cached region locations that map to " + serverName);
284 }
285 }
286 }
287
288
289
290
291 public void clearCache(final TableName tableName) {
292 if (LOG.isTraceEnabled()) {
293 LOG.trace("Removed all cached region locations for table " + tableName);
294 }
295 this.cachedRegionLocations.remove(tableName);
296 }
297
298
299
300
301
302
303 public void clearCache(final TableName tableName, final byte [] row) {
304 ConcurrentMap<byte[], RegionLocations> tableLocations = getTableLocations(tableName);
305
306 RegionLocations regionLocations = getCachedLocation(tableName, row);
307 if (regionLocations != null) {
308 byte[] startKey = regionLocations.getRegionLocation().getRegionInfo().getStartKey();
309 boolean removed = tableLocations.remove(startKey, regionLocations);
310 if (removed) {
311 if (metrics != null) {
312 metrics.incrMetaCacheNumClearRegion();
313 }
314 if (LOG.isTraceEnabled()) {
315 LOG.trace("Removed " + regionLocations + " from cache");
316 }
317 }
318 }
319 }
320
321
322
323
324
325
326
327 public void clearCache(final TableName tableName, final byte [] row, int replicaId) {
328 ConcurrentMap<byte[], RegionLocations> tableLocations = getTableLocations(tableName);
329
330 RegionLocations regionLocations = getCachedLocation(tableName, row);
331 if (regionLocations != null) {
332 HRegionLocation toBeRemoved = regionLocations.getRegionLocation(replicaId);
333 if (toBeRemoved != null) {
334 RegionLocations updatedLocations = regionLocations.remove(replicaId);
335 byte[] startKey = regionLocations.getRegionLocation().getRegionInfo().getStartKey();
336 boolean removed;
337 if (updatedLocations.isEmpty()) {
338 removed = tableLocations.remove(startKey, regionLocations);
339 } else {
340 removed = tableLocations.replace(startKey, regionLocations, updatedLocations);
341 }
342
343 if (removed) {
344 if (metrics != null) {
345 metrics.incrMetaCacheNumClearRegion();
346 }
347 if (LOG.isTraceEnabled()) {
348 LOG.trace("Removed " + toBeRemoved + " from cache");
349 }
350 }
351 }
352 }
353 }
354
355
356
357
358 public void clearCache(final TableName tableName, final byte [] row, ServerName serverName) {
359 ConcurrentMap<byte[], RegionLocations> tableLocations = getTableLocations(tableName);
360
361 RegionLocations regionLocations = getCachedLocation(tableName, row);
362 if (regionLocations != null) {
363 RegionLocations updatedLocations = regionLocations.removeByServer(serverName);
364 if (updatedLocations != regionLocations) {
365 byte[] startKey = regionLocations.getRegionLocation().getRegionInfo().getStartKey();
366 boolean removed = false;
367 if (updatedLocations.isEmpty()) {
368 removed = tableLocations.remove(startKey, regionLocations);
369 } else {
370 removed = tableLocations.replace(startKey, regionLocations, updatedLocations);
371 }
372 if (removed) {
373 if (metrics != null) {
374 metrics.incrMetaCacheNumClearRegion();
375 }
376 if (LOG.isTraceEnabled()) {
377 LOG.trace("Removed locations of table: " + tableName + " ,row: " + Bytes.toString(row)
378 + " mapping to server: " + serverName + " from cache");
379 }
380 }
381 }
382 }
383 }
384
385
386
387
388
389 public void clearCache(HRegionInfo hri) {
390 ConcurrentMap<byte[], RegionLocations> tableLocations = getTableLocations(hri.getTable());
391 RegionLocations regionLocations = tableLocations.get(hri.getStartKey());
392 if (regionLocations != null) {
393 HRegionLocation oldLocation = regionLocations.getRegionLocation(hri.getReplicaId());
394 if (oldLocation == null) return;
395 RegionLocations updatedLocations = regionLocations.remove(oldLocation);
396 boolean removed;
397 if (updatedLocations != regionLocations) {
398 if (updatedLocations.isEmpty()) {
399 removed = tableLocations.remove(hri.getStartKey(), regionLocations);
400 } else {
401 removed = tableLocations.replace(hri.getStartKey(), regionLocations, updatedLocations);
402 }
403 if (removed) {
404 if (metrics != null) {
405 metrics.incrMetaCacheNumClearRegion();
406 }
407 if (LOG.isTraceEnabled()) {
408 LOG.trace("Removed " + oldLocation + " from cache");
409 }
410 }
411 }
412 }
413 }
414
415 public void clearCache(final HRegionLocation location) {
416 if (location == null) {
417 return;
418 }
419 TableName tableName = location.getRegionInfo().getTable();
420 ConcurrentMap<byte[], RegionLocations> tableLocations = getTableLocations(tableName);
421 RegionLocations regionLocations = tableLocations.get(location.getRegionInfo().getStartKey());
422 if (regionLocations != null) {
423 RegionLocations updatedLocations = regionLocations.remove(location);
424 boolean removed;
425 if (updatedLocations != regionLocations) {
426 if (updatedLocations.isEmpty()) {
427 removed = tableLocations.remove(location.getRegionInfo().getStartKey(), regionLocations);
428 } else {
429 removed = tableLocations.replace(location.getRegionInfo().getStartKey(), regionLocations, updatedLocations);
430 }
431 if (removed) {
432 if (metrics != null) {
433 metrics.incrMetaCacheNumClearRegion();
434 }
435 if (LOG.isTraceEnabled()) {
436 LOG.trace("Removed " + location + " from cache");
437 }
438 }
439 }
440 }
441 }
442 }