1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.client;
21
22 import java.io.IOException;
23 import java.lang.reflect.UndeclaredThrowableException;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.LinkedHashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.Set;
31 import java.util.TreeSet;
32 import java.util.concurrent.Callable;
33 import java.util.concurrent.ConcurrentHashMap;
34 import java.util.concurrent.CopyOnWriteArraySet;
35 import java.util.concurrent.ExecutionException;
36 import java.util.concurrent.ExecutorService;
37 import java.util.concurrent.Future;
38 import java.util.concurrent.atomic.AtomicBoolean;
39 import java.util.concurrent.atomic.AtomicInteger;
40
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
43 import org.apache.hadoop.conf.Configuration;
44 import org.apache.hadoop.hbase.DoNotRetryIOException;
45 import org.apache.hadoop.hbase.HConstants;
46 import org.apache.hadoop.hbase.HRegionInfo;
47 import org.apache.hadoop.hbase.HRegionLocation;
48 import org.apache.hadoop.hbase.HServerAddress;
49 import org.apache.hadoop.hbase.HTableDescriptor;
50 import org.apache.hadoop.hbase.KeyValue;
51 import org.apache.hadoop.hbase.MasterAddressTracker;
52 import org.apache.hadoop.hbase.MasterNotRunningException;
53 import org.apache.hadoop.hbase.RemoteExceptionHandler;
54 import org.apache.hadoop.hbase.TableNotFoundException;
55 import org.apache.hadoop.hbase.ZooKeeperConnectionException;
56 import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
57 import org.apache.hadoop.hbase.ipc.HBaseRPC;
58 import org.apache.hadoop.hbase.ipc.HBaseRPCProtocolVersion;
59 import org.apache.hadoop.hbase.ipc.HMasterInterface;
60 import org.apache.hadoop.hbase.ipc.HRegionInterface;
61 import org.apache.hadoop.hbase.util.Bytes;
62 import org.apache.hadoop.hbase.util.Pair;
63 import org.apache.hadoop.hbase.util.SoftValueSortedMap;
64 import org.apache.hadoop.hbase.util.Writables;
65 import org.apache.hadoop.hbase.zookeeper.RootRegionTracker;
66 import org.apache.hadoop.hbase.zookeeper.ZKTable;
67 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
68 import org.apache.hadoop.ipc.RemoteException;
69 import org.apache.zookeeper.KeeperException;
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 @SuppressWarnings("serial")
119 public class HConnectionManager {
120 static final int MAX_CACHED_HBASE_INSTANCES = 2001;
121
122
123
124
125 private static final Map<Configuration, HConnectionImplementation> HBASE_INSTANCES =
126 new LinkedHashMap<Configuration, HConnectionImplementation>
127 ((int) (MAX_CACHED_HBASE_INSTANCES/0.75F)+1, 0.75F, true) {
128 @Override
129 protected boolean removeEldestEntry(Map.Entry<Configuration, HConnectionImplementation> eldest) {
130 return size() > MAX_CACHED_HBASE_INSTANCES;
131 }
132 };
133
134
135
136
137 protected HConnectionManager() {
138 super();
139 }
140
141
142
143
144
145
146
147
148
149
150 public static HConnection getConnection(Configuration conf)
151 throws ZooKeeperConnectionException {
152 HConnectionImplementation connection;
153 synchronized (HBASE_INSTANCES) {
154 connection = HBASE_INSTANCES.get(conf);
155 if (connection == null) {
156 connection = new HConnectionImplementation(conf);
157 HBASE_INSTANCES.put(conf, connection);
158 }
159 }
160 return connection;
161 }
162
163
164
165
166
167
168
169
170
171
172 public static void deleteConnection(Configuration conf, boolean stopProxy) {
173 synchronized (HBASE_INSTANCES) {
174 HConnectionImplementation t = HBASE_INSTANCES.remove(conf);
175 if (t != null) {
176 t.close(stopProxy);
177 }
178 }
179 }
180
181
182
183
184
185
186 public static void deleteAllConnections(boolean stopProxy) {
187 synchronized (HBASE_INSTANCES) {
188 for (HConnectionImplementation t : HBASE_INSTANCES.values()) {
189 if (t != null) {
190 t.close(stopProxy);
191 }
192 }
193 }
194 }
195
196
197
198
199
200
201
202 static int getCachedRegionCount(Configuration conf,
203 byte[] tableName)
204 throws ZooKeeperConnectionException {
205 HConnectionImplementation connection = (HConnectionImplementation)getConnection(conf);
206 return connection.getNumberOfCachedRegionLocations(tableName);
207 }
208
209
210
211
212
213
214
215 static boolean isRegionCached(Configuration conf,
216 byte[] tableName, byte[] row) throws ZooKeeperConnectionException {
217 HConnectionImplementation connection = (HConnectionImplementation)getConnection(conf);
218 return connection.isRegionCached(tableName, row);
219 }
220
221
222 static class HConnectionImplementation implements HConnection {
223 static final Log LOG = LogFactory.getLog(HConnectionImplementation.class);
224 private final Class<? extends HRegionInterface> serverInterfaceClass;
225 private final long pause;
226 private final int numRetries;
227 private final int maxRPCAttempts;
228 private final int rpcTimeout;
229 private final int prefetchRegionLimit;
230
231 private final Object masterLock = new Object();
232 private volatile boolean closed;
233 private volatile HMasterInterface master;
234 private volatile boolean masterChecked;
235
236 private ZooKeeperWatcher zooKeeper;
237
238 private MasterAddressTracker masterAddressTracker;
239 private RootRegionTracker rootRegionTracker;
240
241 private final Object metaRegionLock = new Object();
242
243 private final Object userRegionLock = new Object();
244
245 private final Configuration conf;
246
247
248 private final Map<String, HRegionInterface> servers =
249 new ConcurrentHashMap<String, HRegionInterface>();
250 private final ConcurrentHashMap<String, String> connectionLock = new ConcurrentHashMap<String, String>();
251
252
253
254
255
256 private final Map<Integer, SoftValueSortedMap<byte [], HRegionLocation>>
257 cachedRegionLocations =
258 new HashMap<Integer, SoftValueSortedMap<byte [], HRegionLocation>>();
259
260
261
262 private final Set<Integer> regionCachePrefetchDisabledTables =
263 new CopyOnWriteArraySet<Integer>();
264
265
266
267
268
269 @SuppressWarnings("unchecked")
270 public HConnectionImplementation(Configuration conf)
271 throws ZooKeeperConnectionException {
272 this.conf = conf;
273 String serverClassName = conf.get(HConstants.REGION_SERVER_CLASS,
274 HConstants.DEFAULT_REGION_SERVER_CLASS);
275 this.closed = false;
276 try {
277 this.serverInterfaceClass =
278 (Class<? extends HRegionInterface>) Class.forName(serverClassName);
279 } catch (ClassNotFoundException e) {
280 throw new UnsupportedOperationException(
281 "Unable to find region server interface " + serverClassName, e);
282 }
283
284 this.pause = conf.getLong("hbase.client.pause", 1000);
285 this.numRetries = conf.getInt("hbase.client.retries.number", 10);
286 this.maxRPCAttempts = conf.getInt("hbase.client.rpc.maxattempts", 1);
287 this.rpcTimeout = conf.getInt(
288 HConstants.HBASE_RPC_TIMEOUT_KEY,
289 HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
290
291 this.prefetchRegionLimit = conf.getInt("hbase.client.prefetch.limit",
292 10);
293
294 setupZookeeperTrackers();
295
296 this.master = null;
297 this.masterChecked = false;
298 }
299
300 private synchronized void setupZookeeperTrackers()
301 throws ZooKeeperConnectionException{
302
303 this.zooKeeper = getZooKeeperWatcher();
304 masterAddressTracker = new MasterAddressTracker(this.zooKeeper, this);
305 masterAddressTracker.start();
306
307 this.rootRegionTracker = new RootRegionTracker(this.zooKeeper, this);
308 this.rootRegionTracker.start();
309 }
310
311 private synchronized void resetZooKeeperTrackers()
312 throws ZooKeeperConnectionException {
313 LOG.info("Trying to reconnect to zookeeper");
314 masterAddressTracker.stop();
315 masterAddressTracker = null;
316 rootRegionTracker.stop();
317 rootRegionTracker = null;
318 this.zooKeeper = null;
319 setupZookeeperTrackers();
320 }
321
322 public Configuration getConfiguration() {
323 return this.conf;
324 }
325
326 private long getPauseTime(int tries) {
327 int ntries = tries;
328 if (ntries >= HConstants.RETRY_BACKOFF.length) {
329 ntries = HConstants.RETRY_BACKOFF.length - 1;
330 }
331 return this.pause * HConstants.RETRY_BACKOFF[ntries];
332 }
333
334 public HMasterInterface getMaster()
335 throws MasterNotRunningException, ZooKeeperConnectionException {
336
337
338 if (master != null) {
339 if (master.isMasterRunning()) {
340 return master;
341 }
342 }
343
344 HServerAddress masterLocation = null;
345 synchronized (this.masterLock) {
346 for (int tries = 0;
347 !this.closed &&
348 !this.masterChecked && this.master == null &&
349 tries < numRetries;
350 tries++) {
351
352 try {
353 masterLocation = masterAddressTracker.getMasterAddress();
354 if(masterLocation == null) {
355 LOG.info("ZooKeeper available but no active master location found");
356 throw new MasterNotRunningException();
357 }
358
359 HMasterInterface tryMaster = (HMasterInterface)HBaseRPC.getProxy(
360 HMasterInterface.class, HBaseRPCProtocolVersion.versionID,
361 masterLocation.getInetSocketAddress(), this.conf, this.rpcTimeout);
362
363 if (tryMaster.isMasterRunning()) {
364 this.master = tryMaster;
365 this.masterLock.notifyAll();
366 break;
367 }
368
369 } catch (IOException e) {
370 if (tries == numRetries - 1) {
371
372 LOG.info("getMaster attempt " + tries + " of " + this.numRetries +
373 " failed; no more retrying.", e);
374 break;
375 }
376 LOG.info("getMaster attempt " + tries + " of " + this.numRetries +
377 " failed; retrying after sleep of " +
378 getPauseTime(tries), e);
379 }
380
381
382 try {
383 this.masterLock.wait(getPauseTime(tries));
384 } catch (InterruptedException e) {
385 Thread.currentThread().interrupt();
386 throw new RuntimeException("Thread was interrupted while trying to connect to master.");
387 }
388 }
389 this.masterChecked = true;
390 }
391 if (this.master == null) {
392 if (masterLocation == null) {
393 throw new MasterNotRunningException();
394 }
395 throw new MasterNotRunningException(masterLocation.toString());
396 }
397 return this.master;
398 }
399
400 public boolean isMasterRunning()
401 throws MasterNotRunningException, ZooKeeperConnectionException {
402 if (this.master == null) {
403 getMaster();
404 }
405 boolean isRunning = master.isMasterRunning();
406 if(isRunning) {
407 return true;
408 }
409 throw new MasterNotRunningException();
410 }
411
412 public HRegionLocation getRegionLocation(final byte [] name,
413 final byte [] row, boolean reload)
414 throws IOException {
415 return reload? relocateRegion(name, row): locateRegion(name, row);
416 }
417
418 public HTableDescriptor[] listTables() throws IOException {
419 final TreeSet<HTableDescriptor> uniqueTables =
420 new TreeSet<HTableDescriptor>();
421 MetaScannerVisitor visitor = new MetaScannerVisitor() {
422 public boolean processRow(Result result) throws IOException {
423 try {
424 byte[] value = result.getValue(HConstants.CATALOG_FAMILY,
425 HConstants.REGIONINFO_QUALIFIER);
426 HRegionInfo info = null;
427 if (value != null) {
428 info = Writables.getHRegionInfo(value);
429 }
430
431 if (info != null && info.getStartKey().length == 0) {
432 uniqueTables.add(info.getTableDesc());
433 }
434 return true;
435 } catch (RuntimeException e) {
436 LOG.error("Result=" + result);
437 throw e;
438 }
439 }
440 };
441 MetaScanner.metaScan(conf, visitor);
442 return uniqueTables.toArray(new HTableDescriptor[uniqueTables.size()]);
443 }
444
445 public boolean isTableEnabled(byte[] tableName) throws IOException {
446 return testTableOnlineState(tableName, true);
447 }
448
449 public boolean isTableDisabled(byte[] tableName) throws IOException {
450 return testTableOnlineState(tableName, false);
451 }
452
453 public boolean isTableAvailable(final byte[] tableName) throws IOException {
454 final AtomicBoolean available = new AtomicBoolean(true);
455 final AtomicInteger regionCount = new AtomicInteger(0);
456 MetaScannerVisitor visitor = new MetaScannerVisitor() {
457 @Override
458 public boolean processRow(Result row) throws IOException {
459 byte[] value = row.getValue(HConstants.CATALOG_FAMILY,
460 HConstants.REGIONINFO_QUALIFIER);
461 HRegionInfo info = Writables.getHRegionInfoOrNull(value);
462 if (info != null) {
463 if (Bytes.equals(tableName, info.getTableDesc().getName())) {
464 value = row.getValue(HConstants.CATALOG_FAMILY,
465 HConstants.SERVER_QUALIFIER);
466 if (value == null) {
467 available.set(false);
468 return false;
469 }
470 regionCount.incrementAndGet();
471 }
472 }
473 return true;
474 }
475 };
476 MetaScanner.metaScan(conf, visitor);
477 return available.get() && (regionCount.get() > 0);
478 }
479
480
481
482
483 private boolean testTableOnlineState(byte [] tableName, boolean online)
484 throws IOException {
485 if (Bytes.equals(tableName, HConstants.ROOT_TABLE_NAME)) {
486
487 return online;
488 }
489 String tableNameStr = Bytes.toString(tableName);
490 try {
491 if (online) {
492 return ZKTable.isEnabledTable(this.zooKeeper, tableNameStr);
493 }
494 return ZKTable.isDisabledTable(this.zooKeeper, tableNameStr);
495 } catch (KeeperException e) {
496 throw new IOException("Enable/Disable failed", e);
497 }
498 }
499
500 private static class HTableDescriptorFinder
501 implements MetaScanner.MetaScannerVisitor {
502 byte[] tableName;
503 HTableDescriptor result;
504 protected HTableDescriptorFinder(byte[] tableName) {
505 this.tableName = tableName;
506 }
507 public boolean processRow(Result rowResult) throws IOException {
508 HRegionInfo info = Writables.getHRegionInfoOrNull(
509 rowResult.getValue(HConstants.CATALOG_FAMILY,
510 HConstants.REGIONINFO_QUALIFIER));
511 if (info == null) return true;
512 HTableDescriptor desc = info.getTableDesc();
513 if (Bytes.compareTo(desc.getName(), tableName) == 0) {
514 result = desc;
515 return false;
516 }
517 return true;
518 }
519 HTableDescriptor getResult() {
520 return result;
521 }
522 }
523
524 public HTableDescriptor getHTableDescriptor(final byte[] tableName)
525 throws IOException {
526 if (Bytes.equals(tableName, HConstants.ROOT_TABLE_NAME)) {
527 return new UnmodifyableHTableDescriptor(HTableDescriptor.ROOT_TABLEDESC);
528 }
529 if (Bytes.equals(tableName, HConstants.META_TABLE_NAME)) {
530 return HTableDescriptor.META_TABLEDESC;
531 }
532 HTableDescriptorFinder finder = new HTableDescriptorFinder(tableName);
533 MetaScanner.metaScan(conf, finder, tableName);
534 HTableDescriptor result = finder.getResult();
535 if (result == null) {
536 throw new TableNotFoundException(Bytes.toString(tableName));
537 }
538 return result;
539 }
540
541 @Override
542 public HRegionLocation locateRegion(final byte [] regionName)
543 throws IOException {
544
545 return null;
546 }
547
548 @Override
549 public List<HRegionLocation> locateRegions(final byte [] tableName)
550 throws IOException {
551
552 return null;
553 }
554
555 public HRegionLocation locateRegion(final byte [] tableName,
556 final byte [] row)
557 throws IOException{
558 return locateRegion(tableName, row, true);
559 }
560
561 public HRegionLocation relocateRegion(final byte [] tableName,
562 final byte [] row)
563 throws IOException{
564 return locateRegion(tableName, row, false);
565 }
566
567 private HRegionLocation locateRegion(final byte [] tableName,
568 final byte [] row, boolean useCache)
569 throws IOException {
570 if (this.closed) throw new IOException(toString() + " closed");
571 if (tableName == null || tableName.length == 0) {
572 throw new IllegalArgumentException(
573 "table name cannot be null or zero length");
574 }
575
576 if (Bytes.equals(tableName, HConstants.ROOT_TABLE_NAME)) {
577 try {
578 HServerAddress hsa =
579 this.rootRegionTracker.waitRootRegionLocation(this.rpcTimeout);
580 LOG.debug("Lookedup root region location, connection=" + this +
581 "; hsa=" + hsa);
582 if (hsa == null) return null;
583 return new HRegionLocation(HRegionInfo.ROOT_REGIONINFO, hsa);
584 } catch (InterruptedException e) {
585 Thread.currentThread().interrupt();
586 return null;
587 }
588 } else if (Bytes.equals(tableName, HConstants.META_TABLE_NAME)) {
589 return locateRegionInMeta(HConstants.ROOT_TABLE_NAME, tableName, row,
590 useCache, metaRegionLock);
591 } else {
592
593 return locateRegionInMeta(HConstants.META_TABLE_NAME, tableName, row,
594 useCache, userRegionLock);
595 }
596 }
597
598
599
600
601
602
603 private void prefetchRegionCache(final byte[] tableName,
604 final byte[] row) {
605
606
607 MetaScannerVisitor visitor = new MetaScannerVisitor() {
608 public boolean processRow(Result result) throws IOException {
609 try {
610 byte[] value = result.getValue(HConstants.CATALOG_FAMILY,
611 HConstants.REGIONINFO_QUALIFIER);
612 HRegionInfo regionInfo = null;
613
614 if (value != null) {
615
616 regionInfo = Writables.getHRegionInfo(value);
617
618
619 if (!Bytes.equals(regionInfo.getTableDesc().getName(),
620 tableName)) {
621 return false;
622 }
623 if (regionInfo.isOffline()) {
624
625 return true;
626 }
627 value = result.getValue(HConstants.CATALOG_FAMILY,
628 HConstants.SERVER_QUALIFIER);
629 if (value == null) {
630 return true;
631 }
632 final String serverAddress = Bytes.toString(value);
633
634
635 HRegionLocation loc = new HRegionLocation(regionInfo,
636 new HServerAddress(serverAddress));
637
638 cacheLocation(tableName, loc);
639 }
640 return true;
641 } catch (RuntimeException e) {
642 throw new IOException(e);
643 }
644 }
645 };
646 try {
647
648 MetaScanner.metaScan(conf, visitor, tableName, row,
649 this.prefetchRegionLimit);
650 } catch (IOException e) {
651 LOG.warn("Encountered problems when prefetch META table: ", e);
652 }
653 }
654
655
656
657
658
659 private HRegionLocation locateRegionInMeta(final byte [] parentTable,
660 final byte [] tableName, final byte [] row, boolean useCache,
661 Object regionLockObject)
662 throws IOException {
663 HRegionLocation location;
664
665
666 if (useCache) {
667 location = getCachedLocation(tableName, row);
668 if (location != null) {
669 return location;
670 }
671 }
672
673
674
675
676 byte [] metaKey = HRegionInfo.createRegionName(tableName, row,
677 HConstants.NINES, false);
678 for (int tries = 0; true; tries++) {
679 if (tries >= numRetries) {
680 throw new NoServerForRegionException("Unable to find region for "
681 + Bytes.toStringBinary(row) + " after " + numRetries + " tries.");
682 }
683
684 HRegionLocation metaLocation = null;
685 try {
686
687 metaLocation = locateRegion(parentTable, metaKey);
688
689 if (metaLocation == null) continue;
690 HRegionInterface server =
691 getHRegionConnection(metaLocation.getServerAddress());
692
693 Result regionInfoRow = null;
694
695
696
697 synchronized (regionLockObject) {
698
699
700 if (Bytes.equals(parentTable, HConstants.META_TABLE_NAME) &&
701 (getRegionCachePrefetch(tableName)) ) {
702 prefetchRegionCache(tableName, row);
703 }
704
705
706
707
708
709 if (useCache) {
710 location = getCachedLocation(tableName, row);
711 if (location != null) {
712 return location;
713 }
714 } else {
715 deleteCachedLocation(tableName, row);
716 }
717
718
719 regionInfoRow = server.getClosestRowBefore(
720 metaLocation.getRegionInfo().getRegionName(), metaKey,
721 HConstants.CATALOG_FAMILY);
722 }
723 if (regionInfoRow == null) {
724 throw new TableNotFoundException(Bytes.toString(tableName));
725 }
726 byte[] value = regionInfoRow.getValue(HConstants.CATALOG_FAMILY,
727 HConstants.REGIONINFO_QUALIFIER);
728 if (value == null || value.length == 0) {
729 throw new IOException("HRegionInfo was null or empty in " +
730 Bytes.toString(parentTable) + ", row=" + regionInfoRow);
731 }
732
733 HRegionInfo regionInfo = (HRegionInfo) Writables.getWritable(
734 value, new HRegionInfo());
735
736 if (!Bytes.equals(regionInfo.getTableDesc().getName(), tableName)) {
737 throw new TableNotFoundException(
738 "Table '" + Bytes.toString(tableName) + "' was not found.");
739 }
740 if (regionInfo.isOffline()) {
741 throw new RegionOfflineException("region offline: " +
742 regionInfo.getRegionNameAsString());
743 }
744
745 value = regionInfoRow.getValue(HConstants.CATALOG_FAMILY,
746 HConstants.SERVER_QUALIFIER);
747 String serverAddress = "";
748 if(value != null) {
749 serverAddress = Bytes.toString(value);
750 }
751 if (serverAddress.equals("")) {
752 throw new NoServerForRegionException("No server address listed " +
753 "in " + Bytes.toString(parentTable) + " for region " +
754 regionInfo.getRegionNameAsString());
755 }
756
757
758 location = new HRegionLocation(regionInfo,
759 new HServerAddress(serverAddress));
760 cacheLocation(tableName, location);
761 return location;
762 } catch (TableNotFoundException e) {
763
764
765
766 throw e;
767 } catch (IOException e) {
768 if (e instanceof RemoteException) {
769 e = RemoteExceptionHandler.decodeRemoteException(
770 (RemoteException) e);
771 }
772 if (tries < numRetries - 1) {
773 if (LOG.isDebugEnabled()) {
774 LOG.debug("locateRegionInMeta parentTable=" +
775 Bytes.toString(parentTable) + ", metaLocation=" +
776 ((metaLocation == null)? "null": metaLocation) + ", attempt=" +
777 tries + " of " +
778 this.numRetries + " failed; retrying after sleep of " +
779 getPauseTime(tries) + " because: " + e.getMessage());
780 }
781 } else {
782 throw e;
783 }
784
785 if(!(e instanceof RegionOfflineException ||
786 e instanceof NoServerForRegionException)) {
787 relocateRegion(parentTable, metaKey);
788 }
789 }
790 try{
791 Thread.sleep(getPauseTime(tries));
792 } catch (InterruptedException e) {
793 Thread.currentThread().interrupt();
794 throw new IOException("Giving up trying to location region in " +
795 "meta: thread is interrupted.");
796 }
797 }
798 }
799
800
801
802
803
804
805
806
807
808
809
810
811 HRegionLocation getCachedLocation(final byte [] tableName,
812 final byte [] row) {
813 SoftValueSortedMap<byte [], HRegionLocation> tableLocations =
814 getTableLocations(tableName);
815
816
817
818 if (tableLocations.isEmpty()) {
819 return null;
820 }
821
822 HRegionLocation rl = tableLocations.get(row);
823 if (rl != null) {
824 return rl;
825 }
826
827
828
829 SoftValueSortedMap<byte[], HRegionLocation> matchingRegions =
830 tableLocations.headMap(row);
831
832
833
834
835 if (!matchingRegions.isEmpty()) {
836 HRegionLocation possibleRegion =
837 matchingRegions.get(matchingRegions.lastKey());
838
839
840
841 if (possibleRegion != null) {
842 byte[] endKey = possibleRegion.getRegionInfo().getEndKey();
843
844
845
846
847
848
849 if (Bytes.equals(endKey, HConstants.EMPTY_END_ROW) ||
850 KeyValue.getRowComparator(tableName).compareRows(endKey, 0, endKey.length,
851 row, 0, row.length) > 0) {
852 return possibleRegion;
853 }
854 }
855 }
856
857
858 return null;
859 }
860
861
862
863
864
865
866 void deleteCachedLocation(final byte [] tableName, final byte [] row) {
867 synchronized (this.cachedRegionLocations) {
868 SoftValueSortedMap<byte [], HRegionLocation> tableLocations =
869 getTableLocations(tableName);
870
871
872 if (!tableLocations.isEmpty()) {
873 HRegionLocation rl = getCachedLocation(tableName, row);
874 if (rl != null) {
875 tableLocations.remove(rl.getRegionInfo().getStartKey());
876 if (LOG.isDebugEnabled()) {
877 LOG.debug("Removed " +
878 rl.getRegionInfo().getRegionNameAsString() +
879 " for tableName=" + Bytes.toString(tableName) +
880 " from cache " + "because of " + Bytes.toStringBinary(row));
881 }
882 }
883 }
884 }
885 }
886
887
888
889
890
891 private SoftValueSortedMap<byte [], HRegionLocation> getTableLocations(
892 final byte [] tableName) {
893
894 Integer key = Bytes.mapKey(tableName);
895 SoftValueSortedMap<byte [], HRegionLocation> result;
896 synchronized (this.cachedRegionLocations) {
897 result = this.cachedRegionLocations.get(key);
898
899 if (result == null) {
900 result = new SoftValueSortedMap<byte [], HRegionLocation>(
901 Bytes.BYTES_COMPARATOR);
902 this.cachedRegionLocations.put(key, result);
903 }
904 }
905 return result;
906 }
907
908 @Override
909 public void clearRegionCache() {
910 synchronized(this.cachedRegionLocations) {
911 this.cachedRegionLocations.clear();
912 }
913 }
914
915 @Override
916 public void clearRegionCache(final byte [] tableName) {
917 synchronized (this.cachedRegionLocations) {
918 this.cachedRegionLocations.remove(Bytes.mapKey(tableName));
919 }
920 }
921
922
923
924
925 private void cacheLocation(final byte [] tableName,
926 final HRegionLocation location) {
927 byte [] startKey = location.getRegionInfo().getStartKey();
928 SoftValueSortedMap<byte [], HRegionLocation> tableLocations =
929 getTableLocations(tableName);
930 if (tableLocations.put(startKey, location) == null) {
931 LOG.debug("Cached location for " +
932 location.getRegionInfo().getRegionNameAsString() +
933 " is " + location.getServerAddress());
934 }
935 }
936
937 public HRegionInterface getHRegionConnection(
938 HServerAddress regionServer, boolean getMaster)
939 throws IOException {
940 if (getMaster) {
941 getMaster();
942 }
943 HRegionInterface server;
944 String rsName = regionServer.toString();
945
946 server = this.servers.get(rsName);
947 if (server == null) {
948
949 this.connectionLock.putIfAbsent(rsName, rsName);
950
951 synchronized (this.connectionLock.get(rsName)) {
952
953 server = this.servers.get(rsName);
954 if (server == null) {
955 try {
956
957 server = (HRegionInterface) HBaseRPC.waitForProxy(
958 serverInterfaceClass, HBaseRPCProtocolVersion.versionID,
959 regionServer.getInetSocketAddress(), this.conf,
960 this.maxRPCAttempts, this.rpcTimeout, this.rpcTimeout);
961 this.servers.put(rsName, server);
962 } catch (RemoteException e) {
963 LOG.warn("RemoteException connecting to RS", e);
964
965 throw RemoteExceptionHandler.decodeRemoteException(e);
966 }
967 }
968 }
969 }
970 return server;
971 }
972
973 public HRegionInterface getHRegionConnection(
974 HServerAddress regionServer)
975 throws IOException {
976 return getHRegionConnection(regionServer, false);
977 }
978
979
980
981
982
983
984
985
986 public synchronized ZooKeeperWatcher getZooKeeperWatcher()
987 throws ZooKeeperConnectionException {
988 if(zooKeeper == null) {
989 try {
990 this.zooKeeper = new ZooKeeperWatcher(conf, "hconnection", this);
991 } catch(ZooKeeperConnectionException zce) {
992 throw zce;
993 } catch (IOException e) {
994 throw new ZooKeeperConnectionException("An error is preventing" +
995 " HBase from connecting to ZooKeeper", e);
996 }
997 }
998 return zooKeeper;
999 }
1000
1001 public <T> T getRegionServerWithRetries(ServerCallable<T> callable)
1002 throws IOException, RuntimeException {
1003 List<Throwable> exceptions = new ArrayList<Throwable>();
1004 for(int tries = 0; tries < numRetries; tries++) {
1005 try {
1006 callable.instantiateServer(tries != 0);
1007 return callable.call();
1008 } catch (Throwable t) {
1009 t = translateException(t);
1010 exceptions.add(t);
1011 if (tries == numRetries - 1) {
1012 throw new RetriesExhaustedException(callable.getServerName(),
1013 callable.getRegionName(), callable.getRow(), tries, exceptions);
1014 }
1015 }
1016 try {
1017 Thread.sleep(getPauseTime(tries));
1018 } catch (InterruptedException e) {
1019 Thread.currentThread().interrupt();
1020 throw new IOException("Giving up trying to get region server: thread is interrupted.");
1021 }
1022 }
1023 return null;
1024 }
1025
1026 public <T> T getRegionServerWithoutRetries(ServerCallable<T> callable)
1027 throws IOException, RuntimeException {
1028 try {
1029 callable.instantiateServer(false);
1030 return callable.call();
1031 } catch (Throwable t) {
1032 Throwable t2 = translateException(t);
1033 if (t2 instanceof IOException) {
1034 throw (IOException)t2;
1035 } else {
1036 throw new RuntimeException(t2);
1037 }
1038 }
1039 }
1040
1041 void close(boolean stopProxy) {
1042 if (master != null) {
1043 if (stopProxy) {
1044 HBaseRPC.stopProxy(master);
1045 }
1046 master = null;
1047 masterChecked = false;
1048 }
1049 if (stopProxy) {
1050 for (HRegionInterface i: servers.values()) {
1051 HBaseRPC.stopProxy(i);
1052 }
1053 }
1054 if (this.zooKeeper != null) {
1055 LOG.info("Closed zookeeper sessionid=0x" +
1056 Long.toHexString(this.zooKeeper.getZooKeeper().getSessionId()));
1057 this.zooKeeper.close();
1058 this.zooKeeper = null;
1059 }
1060 this.closed = true;
1061 }
1062
1063 private Callable<MultiResponse> createCallable(
1064 final HServerAddress address,
1065 final MultiAction multi,
1066 final byte [] tableName) {
1067 final HConnection connection = this;
1068 return new Callable<MultiResponse>() {
1069 public MultiResponse call() throws IOException {
1070 return getRegionServerWithoutRetries(
1071 new ServerCallable<MultiResponse>(connection, tableName, null) {
1072 public MultiResponse call() throws IOException {
1073 return server.multi(multi);
1074 }
1075 @Override
1076 public void instantiateServer(boolean reload) throws IOException {
1077 server = connection.getHRegionConnection(address);
1078 }
1079 }
1080 );
1081 }
1082 };
1083 }
1084
1085 public void processBatch(List<Row> list,
1086 final byte[] tableName,
1087 ExecutorService pool,
1088 Object[] results) throws IOException, InterruptedException {
1089
1090
1091 if (results.length != list.size()) {
1092 throw new IllegalArgumentException("argument results must be the same size as argument list");
1093 }
1094
1095 if (list.size() == 0) {
1096 return;
1097 }
1098
1099
1100
1101 HServerAddress [] lastServers = new HServerAddress[results.length];
1102 List<Row> workingList = new ArrayList<Row>(list);
1103 boolean retry = true;
1104 Throwable singleRowCause = null;
1105
1106 for (int tries = 0; tries < numRetries && retry; ++tries) {
1107
1108
1109 if (tries >= 1) {
1110 long sleepTime = getPauseTime(tries);
1111 LOG.debug("Retry " +tries+ ", sleep for " +sleepTime+ "ms!");
1112 Thread.sleep(sleepTime);
1113 }
1114
1115
1116
1117 Map<HServerAddress, MultiAction> actionsByServer = new HashMap<HServerAddress, MultiAction>();
1118 for (int i = 0; i < workingList.size(); i++) {
1119 Row row = workingList.get(i);
1120 if (row != null) {
1121 HRegionLocation loc = locateRegion(tableName, row.getRow(), true);
1122 HServerAddress address = loc.getServerAddress();
1123 byte[] regionName = loc.getRegionInfo().getRegionName();
1124
1125 MultiAction actions = actionsByServer.get(address);
1126 if (actions == null) {
1127 actions = new MultiAction();
1128 actionsByServer.put(address, actions);
1129 }
1130
1131 Action action = new Action(regionName, row, i);
1132 lastServers[i] = address;
1133 actions.add(regionName, action);
1134 }
1135 }
1136
1137
1138
1139 Map<HServerAddress,Future<MultiResponse>> futures =
1140 new HashMap<HServerAddress, Future<MultiResponse>>(actionsByServer.size());
1141
1142 for (Entry<HServerAddress, MultiAction> e : actionsByServer.entrySet()) {
1143 futures.put(e.getKey(), pool.submit(createCallable(e.getKey(), e.getValue(), tableName)));
1144 }
1145
1146
1147
1148 for (Entry<HServerAddress, Future<MultiResponse>> responsePerServer : futures.entrySet()) {
1149 HServerAddress address = responsePerServer.getKey();
1150
1151 try {
1152 Future<MultiResponse> future = responsePerServer.getValue();
1153 MultiResponse resp = future.get();
1154
1155 if (resp == null) {
1156
1157 LOG.debug("Failed all for server: " + address + ", removing from cache");
1158 continue;
1159 }
1160
1161 for (Entry<byte[], List<Pair<Integer,Object>>> e : resp.getResults().entrySet()) {
1162 byte[] regionName = e.getKey();
1163 List<Pair<Integer, Object>> regionResults = e.getValue();
1164 for (Pair<Integer, Object> regionResult : regionResults) {
1165 if (regionResult == null) {
1166
1167 LOG.debug("Failures for region: " +
1168 Bytes.toStringBinary(regionName) +
1169 ", removing from cache");
1170 } else {
1171
1172 results[regionResult.getFirst()] = regionResult.getSecond();
1173 }
1174 }
1175 }
1176 } catch (ExecutionException e) {
1177 LOG.debug("Failed all from " + address, e);
1178 }
1179 }
1180
1181
1182
1183
1184
1185 retry = false;
1186 workingList.clear();
1187 for (int i = 0; i < results.length; i++) {
1188
1189
1190 if (results[i] == null ||
1191 (results[i] instanceof Throwable &&
1192 !(results[i] instanceof DoNotRetryIOException))) {
1193
1194 retry = true;
1195
1196 Row row = list.get(i);
1197 workingList.add(row);
1198 deleteCachedLocation(tableName, row.getRow());
1199 } else {
1200
1201 workingList.add(null);
1202 }
1203 }
1204 }
1205
1206 if (retry) {
1207
1208 if (singleRowCause != null) {
1209 throw new IOException(singleRowCause);
1210 }
1211 }
1212
1213
1214 List<Throwable> exceptions = new ArrayList<Throwable>();
1215 List<Row> actions = new ArrayList<Row>();
1216 List<HServerAddress> addresses = new ArrayList<HServerAddress>();
1217
1218 for (int i = 0 ; i < results.length; i++) {
1219 if (results[i] == null || results[i] instanceof Throwable) {
1220 exceptions.add((Throwable)results[i]);
1221 actions.add(list.get(i));
1222 addresses.add(lastServers[i]);
1223 }
1224 }
1225
1226 if (!exceptions.isEmpty()) {
1227 throw new RetriesExhaustedWithDetailsException(exceptions,
1228 actions,
1229 addresses);
1230 }
1231 }
1232
1233
1234
1235
1236 public void processBatchOfPuts(List<Put> list,
1237 final byte[] tableName,
1238 ExecutorService pool) throws IOException {
1239 Object[] results = new Object[list.size()];
1240 try {
1241 processBatch((List) list, tableName, pool, results);
1242 } catch (InterruptedException e) {
1243 throw new IOException(e);
1244 } finally {
1245
1246
1247
1248
1249 for (int i = results.length - 1; i>=0; i--) {
1250 if (results[i] instanceof Result) {
1251
1252 list.remove(i);
1253 }
1254 }
1255 }
1256 }
1257
1258 private Throwable translateException(Throwable t) throws IOException {
1259 if (t instanceof UndeclaredThrowableException) {
1260 t = t.getCause();
1261 }
1262 if (t instanceof RemoteException) {
1263 t = RemoteExceptionHandler.decodeRemoteException((RemoteException)t);
1264 }
1265 if (t instanceof DoNotRetryIOException) {
1266 throw (DoNotRetryIOException)t;
1267 }
1268 return t;
1269 }
1270
1271
1272
1273
1274
1275 int getNumberOfCachedRegionLocations(final byte[] tableName) {
1276 Integer key = Bytes.mapKey(tableName);
1277 synchronized (this.cachedRegionLocations) {
1278 SoftValueSortedMap<byte[], HRegionLocation> tableLocs =
1279 this.cachedRegionLocations.get(key);
1280
1281 if (tableLocs == null) {
1282 return 0;
1283 }
1284 return tableLocs.values().size();
1285 }
1286 }
1287
1288
1289
1290
1291
1292
1293
1294
1295 boolean isRegionCached(final byte[] tableName, final byte[] row) {
1296 HRegionLocation location = getCachedLocation(tableName, row);
1297 return location != null;
1298 }
1299
1300 public void setRegionCachePrefetch(final byte[] tableName,
1301 final boolean enable) {
1302 if (!enable) {
1303 regionCachePrefetchDisabledTables.add(Bytes.mapKey(tableName));
1304 }
1305 else {
1306 regionCachePrefetchDisabledTables.remove(Bytes.mapKey(tableName));
1307 }
1308 }
1309
1310 public boolean getRegionCachePrefetch(final byte[] tableName) {
1311 return !regionCachePrefetchDisabledTables.contains(Bytes.mapKey(tableName));
1312 }
1313
1314 public void prewarmRegionCache(final byte[] tableName,
1315 final Map<HRegionInfo, HServerAddress> regions) {
1316 for (Map.Entry<HRegionInfo, HServerAddress> e : regions.entrySet()) {
1317 cacheLocation(tableName,
1318 new HRegionLocation(e.getKey(), e.getValue()));
1319 }
1320 }
1321
1322 @Override
1323 public void abort(final String msg, Throwable t) {
1324 if (t instanceof KeeperException.SessionExpiredException) {
1325 try {
1326 LOG.info("This client just lost it's session with ZooKeeper, trying" +
1327 " to reconnect.");
1328 resetZooKeeperTrackers();
1329 LOG.info("Reconnected successfully. This disconnect could have been" +
1330 " caused by a network partition or a long-running GC pause," +
1331 " either way it's recommended that you verify your environment.");
1332 return;
1333 } catch (ZooKeeperConnectionException e) {
1334 LOG.error("Could not reconnect to ZooKeeper after session" +
1335 " expiration, aborting");
1336 t = e;
1337 }
1338 }
1339 if (t != null) LOG.fatal(msg, t);
1340 else LOG.fatal(msg);
1341 this.closed = true;
1342 }
1343 }
1344 }