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.tool;
21
22 import static org.apache.hadoop.hbase.HConstants.DEFAULT_ZOOKEEPER_ZNODE_PARENT;
23 import static org.apache.hadoop.hbase.HConstants.ZOOKEEPER_ZNODE_PARENT;
24
25 import com.google.common.collect.Lists;
26
27 import java.io.Closeable;
28 import java.io.IOException;
29 import java.net.InetSocketAddress;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.LinkedList;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Random;
39 import java.util.Set;
40 import java.util.TreeSet;
41 import java.util.concurrent.Callable;
42 import java.util.concurrent.ConcurrentHashMap;
43 import java.util.concurrent.ExecutionException;
44 import java.util.concurrent.ExecutorService;
45 import java.util.concurrent.Future;
46 import java.util.concurrent.ScheduledThreadPoolExecutor;
47 import java.util.concurrent.atomic.AtomicLong;
48 import java.util.regex.Matcher;
49 import java.util.regex.Pattern;
50
51 import org.apache.commons.lang.time.StopWatch;
52 import org.apache.commons.logging.Log;
53 import org.apache.commons.logging.LogFactory;
54 import org.apache.hadoop.conf.Configuration;
55 import org.apache.hadoop.hbase.AuthUtil;
56 import org.apache.hadoop.hbase.ChoreService;
57 import org.apache.hadoop.hbase.ClusterStatus;
58 import org.apache.hadoop.hbase.DoNotRetryIOException;
59 import org.apache.hadoop.hbase.HBaseConfiguration;
60 import org.apache.hadoop.hbase.HColumnDescriptor;
61 import org.apache.hadoop.hbase.HConstants;
62 import org.apache.hadoop.hbase.HRegionInfo;
63 import org.apache.hadoop.hbase.HRegionLocation;
64 import org.apache.hadoop.hbase.HTableDescriptor;
65 import org.apache.hadoop.hbase.NamespaceDescriptor;
66 import org.apache.hadoop.hbase.ScheduledChore;
67 import org.apache.hadoop.hbase.ServerName;
68 import org.apache.hadoop.hbase.TableName;
69 import org.apache.hadoop.hbase.TableNotEnabledException;
70 import org.apache.hadoop.hbase.TableNotFoundException;
71 import org.apache.hadoop.hbase.classification.InterfaceAudience;
72 import org.apache.hadoop.hbase.client.Admin;
73 import org.apache.hadoop.hbase.client.Connection;
74 import org.apache.hadoop.hbase.client.ConnectionFactory;
75 import org.apache.hadoop.hbase.client.Get;
76 import org.apache.hadoop.hbase.client.Put;
77 import org.apache.hadoop.hbase.client.RegionLocator;
78 import org.apache.hadoop.hbase.client.ResultScanner;
79 import org.apache.hadoop.hbase.client.Scan;
80 import org.apache.hadoop.hbase.client.Table;
81 import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
82 import org.apache.hadoop.hbase.tool.Canary.RegionTask.TaskType;
83 import org.apache.hadoop.hbase.util.Bytes;
84 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
85 import org.apache.hadoop.hbase.util.ReflectionUtils;
86 import org.apache.hadoop.hbase.util.RegionSplitter;
87 import org.apache.hadoop.hbase.zookeeper.EmptyWatcher;
88 import org.apache.hadoop.hbase.zookeeper.ZKConfig;
89 import org.apache.hadoop.util.GenericOptionsParser;
90 import org.apache.hadoop.util.Tool;
91 import org.apache.hadoop.util.ToolRunner;
92 import org.apache.zookeeper.KeeperException;
93 import org.apache.zookeeper.ZooKeeper;
94 import org.apache.zookeeper.client.ConnectStringParser;
95 import org.apache.zookeeper.data.Stat;
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 @InterfaceAudience.Private
112 public final class Canary implements Tool {
113
114 public interface Sink {
115 public long getReadFailureCount();
116 public long incReadFailureCount();
117 public Map<String,String> getReadFailures();
118 public void updateReadFailures(String regionName, String serverName);
119 public long getWriteFailureCount();
120 public long incWriteFailureCount();
121 public Map<String,String> getWriteFailures();
122 public void updateWriteFailures(String regionName, String serverName);
123 }
124
125
126
127 public static class StdOutSink implements Sink {
128 private AtomicLong readFailureCount = new AtomicLong(0),
129 writeFailureCount = new AtomicLong(0);
130
131 private Map<String, String> readFailures = new ConcurrentHashMap<String, String>();
132 private Map<String, String> writeFailures = new ConcurrentHashMap<String, String>();
133
134 @Override
135 public long getReadFailureCount() {
136 return readFailureCount.get();
137 }
138
139 @Override
140 public long incReadFailureCount() {
141 return readFailureCount.incrementAndGet();
142 }
143
144 @Override
145 public Map<String, String> getReadFailures() {
146 return readFailures;
147 }
148
149 @Override
150 public void updateReadFailures(String regionName, String serverName) {
151 readFailures.put(regionName, serverName);
152 }
153
154 @Override
155 public long getWriteFailureCount() {
156 return writeFailureCount.get();
157 }
158
159 @Override
160 public long incWriteFailureCount() {
161 return writeFailureCount.incrementAndGet();
162 }
163
164 @Override
165 public Map<String, String> getWriteFailures() {
166 return writeFailures;
167 }
168
169 @Override
170 public void updateWriteFailures(String regionName, String serverName) {
171 writeFailures.put(regionName, serverName);
172 }
173 }
174
175 public static class RegionServerStdOutSink extends StdOutSink {
176
177 public void publishReadFailure(String table, String server) {
178 incReadFailureCount();
179 LOG.error(String.format("Read from table:%s on region server:%s", table, server));
180 }
181
182 public void publishReadTiming(String table, String server, long msTime) {
183 LOG.info(String.format("Read from table:%s on region server:%s in %dms",
184 table, server, msTime));
185 }
186 }
187
188 public static class ZookeeperStdOutSink extends StdOutSink {
189
190 public void publishReadFailure(String zNode, String server) {
191 incReadFailureCount();
192 LOG.error(String.format("Read from zNode:%s on zookeeper instance:%s", zNode, server));
193 }
194
195 public void publishReadTiming(String znode, String server, long msTime) {
196 LOG.info(String.format("Read from zNode:%s on zookeeper instance:%s in %dms",
197 znode, server, msTime));
198 }
199 }
200
201 public static class RegionStdOutSink extends StdOutSink {
202
203 private Map<String, AtomicLong> perTableReadLatency = new HashMap<>();
204 private AtomicLong writeLatency = new AtomicLong();
205
206 public void publishReadFailure(ServerName serverName, HRegionInfo region, Exception e) {
207 incReadFailureCount();
208 LOG.error(String.format("read from region %s on regionserver %s failed", region.getRegionNameAsString(), serverName), e);
209 }
210
211 public void publishReadFailure(ServerName serverName, HRegionInfo region, HColumnDescriptor column, Exception e) {
212 incReadFailureCount();
213 LOG.error(String.format("read from region %s on regionserver %s column family %s failed",
214 region.getRegionNameAsString(), serverName, column.getNameAsString()), e);
215 }
216
217 public void publishReadTiming(ServerName serverName, HRegionInfo region, HColumnDescriptor column, long msTime) {
218 LOG.info(String.format("read from region %s on regionserver %s column family %s in %dms",
219 region.getRegionNameAsString(), serverName, column.getNameAsString(), msTime));
220 }
221
222 public void publishWriteFailure(ServerName serverName, HRegionInfo region, Exception e) {
223 incWriteFailureCount();
224 LOG.error(String.format("write to region %s on regionserver %s failed", region.getRegionNameAsString(), serverName), e);
225 }
226
227 public void publishWriteFailure(ServerName serverName, HRegionInfo region, HColumnDescriptor column, Exception e) {
228 incWriteFailureCount();
229 LOG.error(String.format("write to region %s on regionserver %s column family %s failed",
230 region.getRegionNameAsString(), serverName, column.getNameAsString()), e);
231 }
232
233 public void publishWriteTiming(ServerName serverName, HRegionInfo region, HColumnDescriptor column, long msTime) {
234 LOG.info(String.format("write to region %s on regionserver %s column family %s in %dms",
235 region.getRegionNameAsString(), serverName, column.getNameAsString(), msTime));
236 }
237
238 public Map<String, AtomicLong> getReadLatencyMap() {
239 return this.perTableReadLatency;
240 }
241
242 public AtomicLong initializeAndGetReadLatencyForTable(String tableName) {
243 AtomicLong initLatency = new AtomicLong(0L);
244 this.perTableReadLatency.put(tableName, initLatency);
245 return initLatency;
246 }
247
248 public void initializeWriteLatency() {
249 this.writeLatency.set(0L);
250 }
251
252 public AtomicLong getWriteLatency() {
253 return this.writeLatency;
254 }
255 }
256
257 static class ZookeeperTask implements Callable<Void> {
258 private final Connection connection;
259 private final String host;
260 private String znode;
261 private final int timeout;
262 private ZookeeperStdOutSink sink;
263
264 public ZookeeperTask(Connection connection, String host, String znode, int timeout,
265 ZookeeperStdOutSink sink) {
266 this.connection = connection;
267 this.host = host;
268 this.znode = znode;
269 this.timeout = timeout;
270 this.sink = sink;
271 }
272
273 @Override public Void call() throws Exception {
274 ZooKeeper zooKeeper = null;
275 try {
276 zooKeeper = new ZooKeeper(host, timeout, EmptyWatcher.instance);
277 Stat exists = zooKeeper.exists(znode, false);
278 StopWatch stopwatch = new StopWatch();
279 stopwatch.start();
280 zooKeeper.getData(znode, false, exists);
281 stopwatch.stop();
282 sink.publishReadTiming(znode, host, stopwatch.getTime());
283 } catch (KeeperException | InterruptedException e) {
284 sink.publishReadFailure(znode, host);
285 } finally {
286 if (zooKeeper != null) {
287 zooKeeper.close();
288 }
289 }
290 return null;
291 }
292 }
293
294
295
296
297
298 static class RegionTask implements Callable<Void> {
299 public enum TaskType{
300 READ, WRITE
301 }
302 private Connection connection;
303 private HRegionInfo region;
304 private RegionStdOutSink sink;
305 private TaskType taskType;
306 private boolean rawScanEnabled;
307 private ServerName serverName;
308 private AtomicLong readWriteLatency;
309
310 RegionTask(Connection connection, HRegionInfo region, ServerName serverName, RegionStdOutSink sink,
311 TaskType taskType, boolean rawScanEnabled, AtomicLong rwLatency) {
312 this.connection = connection;
313 this.region = region;
314 this.serverName = serverName;
315 this.sink = sink;
316 this.taskType = taskType;
317 this.rawScanEnabled = rawScanEnabled;
318 this.readWriteLatency = rwLatency;
319 }
320
321 @Override
322 public Void call() {
323 switch (taskType) {
324 case READ:
325 return read();
326 case WRITE:
327 return write();
328 default:
329 return read();
330 }
331 }
332
333 public Void read() {
334 Table table = null;
335 HTableDescriptor tableDesc = null;
336 try {
337 if (LOG.isDebugEnabled()) {
338 LOG.debug(String.format("reading table descriptor for table %s",
339 region.getTable()));
340 }
341 table = connection.getTable(region.getTable());
342 tableDesc = table.getTableDescriptor();
343 } catch (IOException e) {
344 LOG.debug("sniffRegion failed", e);
345 sink.publishReadFailure(serverName, region, e);
346 if (table != null) {
347 try {
348 table.close();
349 } catch (IOException ioe) {
350 LOG.error("Close table failed", e);
351 }
352 }
353 return null;
354 }
355
356 byte[] startKey = null;
357 Get get = null;
358 Scan scan = null;
359 ResultScanner rs = null;
360 StopWatch stopWatch = new StopWatch();
361 for (HColumnDescriptor column : tableDesc.getColumnFamilies()) {
362 stopWatch.reset();
363 startKey = region.getStartKey();
364
365 if (startKey.length > 0) {
366 get = new Get(startKey);
367 get.setCacheBlocks(false);
368 get.setFilter(new FirstKeyOnlyFilter());
369 get.addFamily(column.getName());
370 } else {
371 scan = new Scan();
372 if (LOG.isDebugEnabled()) {
373 LOG.debug(String.format("rawScan : %s for table: %s", rawScanEnabled,
374 tableDesc.getTableName()));
375 }
376 scan.setRaw(rawScanEnabled);
377 scan.setCaching(1);
378 scan.setCacheBlocks(false);
379 scan.setFilter(new FirstKeyOnlyFilter());
380 scan.addFamily(column.getName());
381 scan.setMaxResultSize(1L);
382 scan.setOneRowLimit();
383 }
384
385 if (LOG.isDebugEnabled()) {
386 LOG.debug(String.format("reading from table %s region %s column family %s and key %s",
387 tableDesc.getTableName(), region.getRegionNameAsString(), column.getNameAsString(),
388 Bytes.toStringBinary(startKey)));
389 }
390 try {
391 stopWatch.start();
392 if (startKey.length > 0) {
393 table.get(get);
394 } else {
395 rs = table.getScanner(scan);
396 rs.next();
397 }
398 stopWatch.stop();
399 this.readWriteLatency.addAndGet(stopWatch.getTime());
400 sink.publishReadTiming(serverName, region, column, stopWatch.getTime());
401 } catch (Exception e) {
402 sink.publishReadFailure(serverName, region, column, e);
403 sink.updateReadFailures(region.getRegionNameAsString(), serverName.getHostname());
404 } finally {
405 if (rs != null) {
406 rs.close();
407 }
408 scan = null;
409 get = null;
410 }
411 }
412 try {
413 table.close();
414 } catch (IOException e) {
415 LOG.error("Close table failed", e);
416 }
417 return null;
418 }
419
420
421
422
423
424 private Void write() {
425 Table table = null;
426 HTableDescriptor tableDesc = null;
427 try {
428 table = connection.getTable(region.getTable());
429 tableDesc = table.getTableDescriptor();
430 byte[] rowToCheck = region.getStartKey();
431 if (rowToCheck.length == 0) {
432 rowToCheck = new byte[]{0x0};
433 }
434 int writeValueSize =
435 connection.getConfiguration().getInt(HConstants.HBASE_CANARY_WRITE_VALUE_SIZE_KEY, 10);
436 for (HColumnDescriptor column : tableDesc.getColumnFamilies()) {
437 Put put = new Put(rowToCheck);
438 byte[] value = new byte[writeValueSize];
439 Bytes.random(value);
440 put.addColumn(column.getName(), HConstants.EMPTY_BYTE_ARRAY, value);
441
442 if (LOG.isDebugEnabled()) {
443 LOG.debug(String.format("writing to table %s region %s column family %s and key %s",
444 tableDesc.getTableName(), region.getRegionNameAsString(), column.getNameAsString(),
445 Bytes.toStringBinary(rowToCheck)));
446 }
447 try {
448 long startTime = System.currentTimeMillis();
449 table.put(put);
450 long time = System.currentTimeMillis() - startTime;
451 this.readWriteLatency.addAndGet(time);
452 sink.publishWriteTiming(serverName, region, column, time);
453 } catch (Exception e) {
454 sink.publishWriteFailure(serverName, region, column, e);
455 }
456 }
457 table.close();
458 } catch (IOException e) {
459 sink.publishWriteFailure(serverName, region, e);
460 sink.updateWriteFailures(region.getRegionNameAsString(), serverName.getHostname() );
461 }
462 return null;
463 }
464 }
465
466
467
468
469 static class RegionServerTask implements Callable<Void> {
470 private Connection connection;
471 private String serverName;
472 private HRegionInfo region;
473 private RegionServerStdOutSink sink;
474 private AtomicLong successes;
475
476 RegionServerTask(Connection connection, String serverName, HRegionInfo region,
477 RegionServerStdOutSink sink, AtomicLong successes) {
478 this.connection = connection;
479 this.serverName = serverName;
480 this.region = region;
481 this.sink = sink;
482 this.successes = successes;
483 }
484
485 @Override
486 public Void call() {
487 TableName tableName = null;
488 Table table = null;
489 Get get = null;
490 byte[] startKey = null;
491 Scan scan = null;
492 StopWatch stopWatch = new StopWatch();
493
494 stopWatch.reset();
495 try {
496 tableName = region.getTable();
497 table = connection.getTable(tableName);
498 startKey = region.getStartKey();
499
500 if (LOG.isDebugEnabled()) {
501 LOG.debug(String.format("reading from region server %s table %s region %s and key %s",
502 serverName, region.getTable(), region.getRegionNameAsString(),
503 Bytes.toStringBinary(startKey)));
504 }
505 if (startKey.length > 0) {
506 get = new Get(startKey);
507 get.setCacheBlocks(false);
508 get.setFilter(new FirstKeyOnlyFilter());
509 stopWatch.start();
510 table.get(get);
511 stopWatch.stop();
512 } else {
513 scan = new Scan();
514 scan.setCacheBlocks(false);
515 scan.setFilter(new FirstKeyOnlyFilter());
516 scan.setCaching(1);
517 scan.setMaxResultSize(1L);
518 scan.setOneRowLimit();
519 stopWatch.start();
520 ResultScanner s = table.getScanner(scan);
521 s.next();
522 s.close();
523 stopWatch.stop();
524 }
525 successes.incrementAndGet();
526 sink.publishReadTiming(tableName.getNameAsString(), serverName, stopWatch.getTime());
527 } catch (TableNotFoundException tnfe) {
528 LOG.error("Table may be deleted", tnfe);
529
530 } catch (TableNotEnabledException tnee) {
531
532 successes.incrementAndGet();
533 LOG.debug("The targeted table was disabled. Assuming success.");
534 } catch (DoNotRetryIOException dnrioe) {
535 sink.publishReadFailure(tableName.getNameAsString(), serverName);
536 LOG.error(dnrioe);
537 } catch (IOException e) {
538 sink.publishReadFailure(tableName.getNameAsString(), serverName);
539 LOG.error(e);
540 } finally {
541 if (table != null) {
542 try {
543 table.close();
544 } catch (IOException e) {
545 LOG.error("Close table failed", e);
546 }
547 }
548 scan = null;
549 get = null;
550 startKey = null;
551 }
552 return null;
553 }
554 }
555
556 private static final int USAGE_EXIT_CODE = 1;
557 private static final int INIT_ERROR_EXIT_CODE = 2;
558 private static final int TIMEOUT_ERROR_EXIT_CODE = 3;
559 private static final int ERROR_EXIT_CODE = 4;
560 private static final int FAILURE_EXIT_CODE = 5;
561
562 private static final long DEFAULT_INTERVAL = 60000;
563
564 private static final long DEFAULT_TIMEOUT = 600000;
565 private static final int MAX_THREADS_NUM = 16;
566
567 private static final Log LOG = LogFactory.getLog(Canary.class);
568
569 public static final TableName DEFAULT_WRITE_TABLE_NAME = TableName.valueOf(
570 NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "canary");
571
572 private static final String CANARY_TABLE_FAMILY_NAME = "Test";
573
574 private Configuration conf = null;
575 private long interval = 0;
576 private Sink sink = null;
577
578 private boolean useRegExp;
579 private long timeout = DEFAULT_TIMEOUT;
580 private boolean failOnError = true;
581 private boolean regionServerMode = false;
582 private boolean zookeeperMode = false;
583 private long permittedFailures = 0;
584 private boolean regionServerAllRegions = false;
585 private boolean writeSniffing = false;
586 private long configuredWriteTableTimeout = DEFAULT_TIMEOUT;
587 private boolean treatFailureAsError = false;
588 private TableName writeTableName = DEFAULT_WRITE_TABLE_NAME;
589 private HashMap<String, Long> configuredReadTableTimeouts = new HashMap<>();
590
591 private ExecutorService executor;
592
593 public Canary() {
594 this(new ScheduledThreadPoolExecutor(1), new RegionServerStdOutSink());
595 }
596
597 public Canary(ExecutorService executor, Sink sink) {
598 this.executor = executor;
599 this.sink = sink;
600 }
601
602 @Override
603 public Configuration getConf() {
604 return conf;
605 }
606
607 @Override
608 public void setConf(Configuration conf) {
609 this.conf = conf;
610 }
611
612 private int parseArgs(String[] args) {
613 int index = -1;
614
615 for (int i = 0; i < args.length; i++) {
616 String cmd = args[i];
617
618 if (cmd.startsWith("-")) {
619 if (index >= 0) {
620
621 System.err.println("Invalid command line options");
622 printUsageAndExit();
623 }
624
625 if (cmd.equals("-help")) {
626
627 printUsageAndExit();
628 } else if (cmd.equals("-daemon") && interval == 0) {
629
630 interval = DEFAULT_INTERVAL;
631 } else if (cmd.equals("-interval")) {
632
633 i++;
634
635 if (i == args.length) {
636 System.err.println("-interval needs a numeric value argument.");
637 printUsageAndExit();
638 }
639
640 try {
641 interval = Long.parseLong(args[i]) * 1000;
642 } catch (NumberFormatException e) {
643 System.err.println("-interval needs a numeric value argument.");
644 printUsageAndExit();
645 }
646 } else if (cmd.equals("-zookeeper")) {
647 this.zookeeperMode = true;
648 } else if(cmd.equals("-regionserver")) {
649 this.regionServerMode = true;
650 } else if(cmd.equals("-allRegions")) {
651 this.regionServerAllRegions = true;
652 } else if(cmd.equals("-writeSniffing")) {
653 this.writeSniffing = true;
654 } else if(cmd.equals("-treatFailureAsError")) {
655 this.treatFailureAsError = true;
656 } else if (cmd.equals("-e")) {
657 this.useRegExp = true;
658 } else if (cmd.equals("-t")) {
659 i++;
660
661 if (i == args.length) {
662 System.err.println("-t needs a numeric value argument.");
663 printUsageAndExit();
664 }
665
666 try {
667 this.timeout = Long.parseLong(args[i]);
668 } catch (NumberFormatException e) {
669 System.err.println("-t needs a numeric value argument.");
670 printUsageAndExit();
671 }
672 } else if(cmd.equals("-writeTableTimeout")) {
673 i++;
674
675 if (i == args.length) {
676 System.err.println("-writeTableTimeout needs a numeric value argument.");
677 printUsageAndExit();
678 }
679
680 try {
681 this.configuredWriteTableTimeout = Long.parseLong(args[i]);
682 } catch (NumberFormatException e) {
683 System.err.println("-writeTableTimeout needs a numeric value argument.");
684 printUsageAndExit();
685 }
686 } else if (cmd.equals("-writeTable")) {
687 i++;
688
689 if (i == args.length) {
690 System.err.println("-writeTable needs a string value argument.");
691 printUsageAndExit();
692 }
693 this.writeTableName = TableName.valueOf(args[i]);
694 } else if (cmd.equals("-f")) {
695 i++;
696
697 if (i == args.length) {
698 System.err
699 .println("-f needs a boolean value argument (true|false).");
700 printUsageAndExit();
701 }
702
703 this.failOnError = Boolean.parseBoolean(args[i]);
704 } else if (cmd.equals("-readTableTimeouts")) {
705 i++;
706
707 if (i == args.length) {
708 System.err.println("-readTableTimeouts needs a comma-separated list of read timeouts per table (without spaces).");
709 printUsageAndExit();
710 }
711 String [] tableTimeouts = args[i].split(",");
712 for (String tT: tableTimeouts) {
713 String [] nameTimeout = tT.split("=");
714 if (nameTimeout.length < 2) {
715 System.err.println("Each -readTableTimeouts argument must be of the form <tableName>=<read timeout>.");
716 printUsageAndExit();
717 }
718 long timeoutVal = 0L;
719 try {
720 timeoutVal = Long.parseLong(nameTimeout[1]);
721 } catch (NumberFormatException e) {
722 System.err.println("-readTableTimeouts read timeout for each table must be a numeric value argument.");
723 printUsageAndExit();
724 }
725 this.configuredReadTableTimeouts.put(nameTimeout[0], timeoutVal);
726 }
727 } else if (cmd.equals("-permittedZookeeperFailures")) {
728 i++;
729
730 if (i == args.length) {
731 System.err.println("-permittedZookeeperFailures needs a numeric value argument.");
732 printUsageAndExit();
733 }
734 try {
735 this.permittedFailures = Long.parseLong(args[i]);
736 } catch (NumberFormatException e) {
737 System.err.println("-permittedZookeeperFailures needs a numeric value argument.");
738 printUsageAndExit();
739 }
740 } else {
741
742 System.err.println(cmd + " options is invalid.");
743 printUsageAndExit();
744 }
745 } else if (index < 0) {
746
747 index = i;
748 }
749 }
750 if (this.regionServerAllRegions && !this.regionServerMode) {
751 System.err.println("-allRegions can only be specified in regionserver mode.");
752 printUsageAndExit();
753 }
754 if (this.zookeeperMode) {
755 if (this.regionServerMode || this.regionServerAllRegions || this.writeSniffing) {
756 System.err.println("-zookeeper is exclusive and cannot be combined with "
757 + "other modes.");
758 printUsageAndExit();
759 }
760 }
761 if (this.permittedFailures != 0 && !this.zookeeperMode) {
762 System.err.println("-permittedZookeeperFailures requires -zookeeper mode.");
763 printUsageAndExit();
764 }
765 if (!this.configuredReadTableTimeouts.isEmpty() && (this.regionServerMode || this.zookeeperMode)) {
766 System.err.println("-readTableTimeouts can only be configured in region mode.");
767 printUsageAndExit();
768 }
769 return index;
770 }
771
772 @Override
773 public int run(String[] args) throws Exception {
774 int index = parseArgs(args);
775 ChoreService choreService = null;
776
777
778
779
780 final ScheduledChore authChore = AuthUtil.getAuthChore(conf);
781 if (authChore != null) {
782 choreService = new ChoreService("CANARY_TOOL");
783 choreService.scheduleChore(authChore);
784 }
785
786
787 Monitor monitor = null;
788 Thread monitorThread = null;
789 long startTime = 0;
790 long currentTimeLength = 0;
791
792 try (Connection connection = ConnectionFactory.createConnection(this.conf)) {
793 do {
794
795 try {
796 monitor = this.newMonitor(connection, index, args);
797 monitorThread = new Thread(monitor, "CanaryMonitor-" + System.currentTimeMillis());
798 startTime = System.currentTimeMillis();
799 monitorThread.start();
800 while (!monitor.isDone()) {
801
802 Thread.sleep(1000);
803
804 if (this.failOnError && monitor.hasError()) {
805 monitorThread.interrupt();
806 if (monitor.initialized) {
807 return monitor.errorCode;
808 } else {
809 return INIT_ERROR_EXIT_CODE;
810 }
811 }
812 currentTimeLength = System.currentTimeMillis() - startTime;
813 if (currentTimeLength > this.timeout) {
814 LOG.error("The monitor is running too long (" + currentTimeLength
815 + ") after timeout limit:" + this.timeout
816 + " will be killed itself !!");
817 if (monitor.initialized) {
818 return TIMEOUT_ERROR_EXIT_CODE;
819 } else {
820 return INIT_ERROR_EXIT_CODE;
821 }
822 }
823 }
824
825 if (this.failOnError && monitor.finalCheckForErrors()) {
826 monitorThread.interrupt();
827 return monitor.errorCode;
828 }
829 } finally {
830 if (monitor != null) monitor.close();
831 }
832
833 Thread.sleep(interval);
834 } while (interval > 0);
835 }
836
837 if (choreService != null) {
838 choreService.shutdown();
839 }
840 return monitor.errorCode;
841 }
842
843 public Map<String, String> getReadFailures() {
844 return sink.getReadFailures();
845 }
846
847 public Map<String, String> getWriteFailures() {
848 return sink.getWriteFailures();
849 }
850
851 private void printUsageAndExit() {
852 System.err.printf(
853 "Usage: bin/hbase %s [opts] [table1 [table2]...] | [regionserver1 [regionserver2]..]%n",
854 getClass().getName());
855 System.err.println(" where [opts] are:");
856 System.err.println(" -help Show this help and exit.");
857 System.err.println(" -regionserver replace the table argument to regionserver,");
858 System.err.println(" which means to enable regionserver mode");
859 System.err.println(" -allRegions Tries all regions on a regionserver,");
860 System.err.println(" only works in regionserver mode.");
861 System.err.println(" -zookeeper Tries to grab zookeeper.znode.parent ");
862 System.err.println(" on each zookeeper instance");
863 System.err.println(" -permittedZookeeperFailures <N> Ignore first N failures when attempting to ");
864 System.err.println(" connect to individual zookeeper nodes in the ensemble");
865 System.err.println(" -daemon Continuous check at defined intervals.");
866 System.err.println(" -interval <N> Interval between checks (sec)");
867 System.err.println(" -e Use table/regionserver as regular expression");
868 System.err.println(" which means the table/regionserver is regular expression pattern");
869 System.err.println(" -f <B> stop whole program if first error occurs," +
870 " default is true");
871 System.err.println(" -t <N> timeout for a check, default is 600000 (millisecs)");
872 System.err.println(" -writeTableTimeout <N> write timeout for the writeTable, default is 600000 (millisecs)");
873 System.err.println(" -readTableTimeouts <tableName>=<read timeout>,<tableName>=<read timeout>, ... "
874 + "comma-separated list of read timeouts per table (no spaces), default is 600000 (millisecs)");
875 System.err.println(" -writeSniffing enable the write sniffing in canary");
876 System.err.println(" -treatFailureAsError treats read / write failure as error");
877 System.err.println(" -writeTable The table used for write sniffing."
878 + " Default is hbase:canary");
879 System.err.println(" -Dhbase.canary.read.raw.enabled=<true/false> Use this flag to enable or disable raw scan during read canary test"
880 + " Default is false and raw is not enabled during scan");
881 System.err
882 .println(" -D<configProperty>=<value> assigning or override the configuration params");
883 System.exit(USAGE_EXIT_CODE);
884 }
885
886
887
888
889
890
891
892
893 public Monitor newMonitor(final Connection connection, int index, String[] args) {
894 Monitor monitor = null;
895 String[] monitorTargets = null;
896
897 if(index >= 0) {
898 int length = args.length - index;
899 monitorTargets = new String[length];
900 System.arraycopy(args, index, monitorTargets, 0, length);
901 }
902
903 if (this.sink instanceof RegionServerStdOutSink || this.regionServerMode) {
904 monitor =
905 new RegionServerMonitor(connection, monitorTargets, this.useRegExp,
906 (StdOutSink) this.sink, this.executor, this.regionServerAllRegions,
907 this.treatFailureAsError, this.permittedFailures);
908 } else if (this.sink instanceof ZookeeperStdOutSink || this.zookeeperMode) {
909 monitor =
910 new ZookeeperMonitor(connection, monitorTargets, this.useRegExp,
911 (StdOutSink) this.sink, this.executor, this.treatFailureAsError,
912 this.permittedFailures);
913 } else {
914 monitor =
915 new RegionMonitor(connection, monitorTargets, this.useRegExp,
916 (StdOutSink) this.sink, this.executor, this.writeSniffing,
917 this.writeTableName, this.treatFailureAsError, this.configuredReadTableTimeouts,
918 this.configuredWriteTableTimeout, this.permittedFailures);
919 }
920 return monitor;
921 }
922
923
924 public static abstract class Monitor implements Runnable, Closeable {
925
926 protected Connection connection;
927 protected Admin admin;
928 protected String[] targets;
929 protected boolean useRegExp;
930 protected boolean treatFailureAsError;
931 protected boolean initialized = false;
932
933 protected boolean done = false;
934 protected int errorCode = 0;
935 protected long allowedFailures = 0;
936 protected Sink sink;
937 protected ExecutorService executor;
938
939 public boolean isDone() {
940 return done;
941 }
942
943 public boolean hasError() {
944 return errorCode != 0;
945 }
946
947 public boolean finalCheckForErrors() {
948 if (errorCode != 0) {
949 return true;
950 }
951 if (treatFailureAsError &&
952 (sink.getReadFailureCount() > allowedFailures || sink.getWriteFailureCount() > allowedFailures)) {
953 LOG.error("Too many failures detected, treating failure as error, failing the Canary.");
954 errorCode = FAILURE_EXIT_CODE;
955 return true;
956 }
957 return false;
958 }
959
960 @Override
961 public void close() throws IOException {
962 if (this.admin != null) this.admin.close();
963 }
964
965 protected Monitor(Connection connection, String[] monitorTargets, boolean useRegExp, Sink sink,
966 ExecutorService executor, boolean treatFailureAsError, long allowedFailures) {
967 if (null == connection) throw new IllegalArgumentException("connection shall not be null");
968
969 this.connection = connection;
970 this.targets = monitorTargets;
971 this.useRegExp = useRegExp;
972 this.treatFailureAsError = treatFailureAsError;
973 this.sink = sink;
974 this.executor = executor;
975 this.allowedFailures = allowedFailures;
976 }
977
978 @Override
979 public abstract void run();
980
981 protected boolean initAdmin() {
982 if (null == this.admin) {
983 try {
984 this.admin = this.connection.getAdmin();
985 } catch (Exception e) {
986 LOG.error("Initial HBaseAdmin failed...", e);
987 this.errorCode = INIT_ERROR_EXIT_CODE;
988 }
989 } else if (admin.isAborted()) {
990 LOG.error("HBaseAdmin aborted");
991 this.errorCode = INIT_ERROR_EXIT_CODE;
992 }
993 return !this.hasError();
994 }
995 }
996
997
998 private static class RegionMonitor extends Monitor {
999
1000 private static final int DEFAULT_WRITE_TABLE_CHECK_PERIOD = 10 * 60 * 1000;
1001
1002 private static final int DEFAULT_WRITE_DATA_TTL = 24 * 60 * 60;
1003
1004 private long lastCheckTime = -1;
1005 private boolean writeSniffing;
1006 private TableName writeTableName;
1007 private int writeDataTTL;
1008 private float regionsLowerLimit;
1009 private float regionsUpperLimit;
1010 private int checkPeriod;
1011 private boolean rawScanEnabled;
1012 private HashMap<String, Long> configuredReadTableTimeouts;
1013 private long configuredWriteTableTimeout;
1014
1015 public RegionMonitor(Connection connection, String[] monitorTargets, boolean useRegExp,
1016 StdOutSink sink, ExecutorService executor, boolean writeSniffing, TableName writeTableName,
1017 boolean treatFailureAsError, HashMap<String, Long> configuredReadTableTimeouts,
1018 long configuredWriteTableTimeout, long allowedFailures) {
1019 super(connection, monitorTargets, useRegExp, sink, executor, treatFailureAsError, allowedFailures);
1020 Configuration conf = connection.getConfiguration();
1021 this.writeSniffing = writeSniffing;
1022 this.writeTableName = writeTableName;
1023 this.writeDataTTL =
1024 conf.getInt(HConstants.HBASE_CANARY_WRITE_DATA_TTL_KEY, DEFAULT_WRITE_DATA_TTL);
1025 this.regionsLowerLimit =
1026 conf.getFloat(HConstants.HBASE_CANARY_WRITE_PERSERVER_REGIONS_LOWERLIMIT_KEY, 1.0f);
1027 this.regionsUpperLimit =
1028 conf.getFloat(HConstants.HBASE_CANARY_WRITE_PERSERVER_REGIONS_UPPERLIMIT_KEY, 1.5f);
1029 this.checkPeriod =
1030 conf.getInt(HConstants.HBASE_CANARY_WRITE_TABLE_CHECK_PERIOD_KEY,
1031 DEFAULT_WRITE_TABLE_CHECK_PERIOD);
1032 this.rawScanEnabled = conf.getBoolean(HConstants.HBASE_CANARY_READ_RAW_SCAN_KEY, false);
1033 this.configuredReadTableTimeouts = new HashMap<>(configuredReadTableTimeouts);
1034 this.configuredWriteTableTimeout = configuredWriteTableTimeout;
1035 }
1036
1037 private RegionStdOutSink getSink() {
1038 if (!(sink instanceof RegionStdOutSink)) {
1039 throw new RuntimeException("Can only write to Region sink");
1040 }
1041 return ((RegionStdOutSink) sink);
1042 }
1043
1044 @Override
1045 public void run() {
1046 if (this.initAdmin()) {
1047 try {
1048 List<Future<Void>> taskFutures = new LinkedList<>();
1049 RegionStdOutSink regionSink = this.getSink();
1050 if (this.targets != null && this.targets.length > 0) {
1051 String[] tables = generateMonitorTables(this.targets);
1052
1053 if (! new HashSet<>(Arrays.asList(tables)).containsAll(this.configuredReadTableTimeouts.keySet())) {
1054 LOG.error("-readTableTimeouts can only specify read timeouts for monitor targets passed via command line.");
1055 this.errorCode = USAGE_EXIT_CODE;
1056 return;
1057 }
1058 this.initialized = true;
1059 for (String table : tables) {
1060 AtomicLong readLatency = regionSink.initializeAndGetReadLatencyForTable(table);
1061 taskFutures.addAll(Canary.sniff(admin, regionSink, table, executor, TaskType.READ,
1062 this.rawScanEnabled, readLatency));
1063 }
1064 } else {
1065 taskFutures.addAll(sniff(TaskType.READ, regionSink));
1066 }
1067
1068 if (writeSniffing) {
1069 if (EnvironmentEdgeManager.currentTime() - lastCheckTime > checkPeriod) {
1070 try {
1071 checkWriteTableDistribution();
1072 } catch (IOException e) {
1073 LOG.error("Check canary table distribution failed!", e);
1074 }
1075 lastCheckTime = EnvironmentEdgeManager.currentTime();
1076 }
1077
1078 regionSink.initializeWriteLatency();
1079 AtomicLong writeTableLatency = regionSink.getWriteLatency();
1080 taskFutures.addAll(Canary.sniff(admin, regionSink, admin.getTableDescriptor(writeTableName),
1081 executor, TaskType.WRITE, this.rawScanEnabled, writeTableLatency));
1082 }
1083
1084 for (Future<Void> future : taskFutures) {
1085 try {
1086 future.get();
1087 } catch (ExecutionException e) {
1088 LOG.error("Sniff region failed!", e);
1089 }
1090 }
1091 Map<String, AtomicLong> actualReadTableLatency = regionSink.getReadLatencyMap();
1092 for (Map.Entry<String, Long> entry : configuredReadTableTimeouts.entrySet()) {
1093 String tableName = entry.getKey();
1094 if (actualReadTableLatency.containsKey(tableName)) {
1095 Long actual = actualReadTableLatency.get(tableName).longValue();
1096 Long configured = entry.getValue();
1097 LOG.info("Read operation for " + tableName + " took " + actual +
1098 " ms. The configured read timeout was " + configured + " ms.");
1099 if (actual > configured) {
1100 LOG.error("Read operation for " + tableName + " exceeded the configured read timeout.");
1101 }
1102 } else {
1103 LOG.error("Read operation for " + tableName + " failed!");
1104 }
1105 }
1106 if (this.writeSniffing) {
1107 String writeTableStringName = this.writeTableName.getNameAsString();
1108 long actualWriteLatency = regionSink.getWriteLatency().longValue();
1109 LOG.info("Write operation for " + writeTableStringName + " took " + actualWriteLatency + " ms. The configured write timeout was " +
1110 this.configuredWriteTableTimeout + " ms.");
1111
1112 if (actualWriteLatency > this.configuredWriteTableTimeout) {
1113 LOG.error("Write operation for " + writeTableStringName + " exceeded the configured write timeout.");
1114 }
1115 }
1116 } catch (Exception e) {
1117 LOG.error("Run regionMonitor failed", e);
1118 this.errorCode = ERROR_EXIT_CODE;
1119 } finally {
1120 this.done = true;
1121 }
1122 }
1123 this.done = true;
1124 }
1125
1126 private String[] generateMonitorTables(String[] monitorTargets) throws IOException {
1127 String[] returnTables = null;
1128
1129 if (this.useRegExp) {
1130 Pattern pattern = null;
1131 HTableDescriptor[] tds = null;
1132 Set<String> tmpTables = new TreeSet<String>();
1133 try {
1134 if (LOG.isDebugEnabled()) {
1135 LOG.debug(String.format("reading list of tables"));
1136 }
1137 tds = this.admin.listTables(pattern);
1138 if (tds == null) {
1139 tds = new HTableDescriptor[0];
1140 }
1141 for (String monitorTarget : monitorTargets) {
1142 pattern = Pattern.compile(monitorTarget);
1143 for (HTableDescriptor td : tds) {
1144 if (pattern.matcher(td.getNameAsString()).matches()) {
1145 tmpTables.add(td.getNameAsString());
1146 }
1147 }
1148 }
1149 } catch (IOException e) {
1150 LOG.error("Communicate with admin failed", e);
1151 throw e;
1152 }
1153
1154 if (tmpTables.size() > 0) {
1155 returnTables = tmpTables.toArray(new String[tmpTables.size()]);
1156 } else {
1157 String msg = "No HTable found, tablePattern:" + Arrays.toString(monitorTargets);
1158 LOG.error(msg);
1159 this.errorCode = INIT_ERROR_EXIT_CODE;
1160 throw new TableNotFoundException(msg);
1161 }
1162 } else {
1163 returnTables = monitorTargets;
1164 }
1165
1166 return returnTables;
1167 }
1168
1169
1170
1171
1172 private List<Future<Void>> sniff(TaskType taskType, RegionStdOutSink regionSink) throws Exception {
1173 if (LOG.isDebugEnabled()) {
1174 LOG.debug(String.format("reading list of tables"));
1175 }
1176 List<Future<Void>> taskFutures = new LinkedList<>();
1177 for (HTableDescriptor table : admin.listTables()) {
1178 if (admin.isTableEnabled(table.getTableName())
1179 && (!table.getTableName().equals(writeTableName))) {
1180 AtomicLong readLatency = regionSink.initializeAndGetReadLatencyForTable(table.getNameAsString());
1181 taskFutures.addAll(Canary.sniff(admin, sink, table, executor, taskType, this.rawScanEnabled, readLatency));
1182 }
1183 }
1184 return taskFutures;
1185 }
1186
1187 private void checkWriteTableDistribution() throws IOException {
1188 if (!admin.tableExists(writeTableName)) {
1189 int numberOfServers = admin.getClusterStatus().getServers().size();
1190 if (numberOfServers == 0) {
1191 throw new IllegalStateException("No live regionservers");
1192 }
1193 createWriteTable(numberOfServers);
1194 }
1195
1196 if (!admin.isTableEnabled(writeTableName)) {
1197 admin.enableTable(writeTableName);
1198 }
1199
1200 ClusterStatus status = admin.getClusterStatus();
1201 int numberOfServers = status.getServersSize();
1202 if (status.getServers().contains(status.getMaster())) {
1203 numberOfServers -= 1;
1204 }
1205
1206 List<HRegionLocation> locations;
1207 RegionLocator locator = connection.getRegionLocator(writeTableName);
1208 try {
1209 locations = locator.getAllRegionLocations();
1210 } finally {
1211 locator.close();
1212 }
1213 int numberOfRegions = locations.size();
1214 if (numberOfRegions < numberOfServers * regionsLowerLimit
1215 || numberOfRegions > numberOfServers * regionsUpperLimit) {
1216 admin.disableTable(writeTableName);
1217 admin.deleteTable(writeTableName);
1218 createWriteTable(numberOfServers);
1219 }
1220 HashSet<ServerName> serverSet = new HashSet<ServerName>();
1221 for (HRegionLocation location: locations) {
1222 serverSet.add(location.getServerName());
1223 }
1224 int numberOfCoveredServers = serverSet.size();
1225 if (numberOfCoveredServers < numberOfServers) {
1226 admin.balancer();
1227 }
1228 }
1229
1230 private void createWriteTable(int numberOfServers) throws IOException {
1231 int numberOfRegions = (int)(numberOfServers * regionsLowerLimit);
1232 LOG.info("Number of live regionservers: " + numberOfServers + ", "
1233 + "pre-splitting the canary table into " + numberOfRegions + " regions "
1234 + "(current lower limit of regions per server is " + regionsLowerLimit
1235 + " and you can change it by config: "
1236 + HConstants.HBASE_CANARY_WRITE_PERSERVER_REGIONS_LOWERLIMIT_KEY + " )");
1237 HTableDescriptor desc = new HTableDescriptor(writeTableName);
1238 HColumnDescriptor family = new HColumnDescriptor(CANARY_TABLE_FAMILY_NAME);
1239 family.setMaxVersions(1);
1240 family.setTimeToLive(writeDataTTL);
1241
1242 desc.addFamily(family);
1243 byte[][] splits = new RegionSplitter.HexStringSplit().split(numberOfRegions);
1244 admin.createTable(desc, splits);
1245 }
1246 }
1247
1248
1249
1250
1251
1252 private static List<Future<Void>> sniff(final Admin admin, final Sink sink, String tableName,
1253 ExecutorService executor, TaskType taskType, boolean rawScanEnabled, AtomicLong readLatency) throws Exception {
1254 if (LOG.isDebugEnabled()) {
1255 LOG.debug(String.format("checking table is enabled and getting table descriptor for table %s",
1256 tableName));
1257 }
1258 if (admin.isTableEnabled(TableName.valueOf(tableName))) {
1259 return Canary.sniff(admin, sink, admin.getTableDescriptor(TableName.valueOf(tableName)),
1260 executor, taskType, rawScanEnabled, readLatency);
1261 } else {
1262 LOG.warn(String.format("Table %s is not enabled", tableName));
1263 }
1264 return new LinkedList<Future<Void>>();
1265 }
1266
1267
1268
1269
1270 private static List<Future<Void>> sniff(final Admin admin, final Sink sink,
1271 HTableDescriptor tableDesc, ExecutorService executor, TaskType taskType,
1272 boolean rawScanEnabled, AtomicLong rwLatency) throws Exception {
1273
1274 if (LOG.isDebugEnabled()) {
1275 LOG.debug(String.format("reading list of regions for table %s", tableDesc.getTableName()));
1276 }
1277
1278 Table table = null;
1279 try {
1280 table = admin.getConnection().getTable(tableDesc.getTableName());
1281 } catch (TableNotFoundException e) {
1282 return new ArrayList<Future<Void>>();
1283 } finally {
1284 if (table != null) {
1285 table.close();
1286 }
1287 }
1288
1289 List<RegionTask> tasks = new ArrayList<RegionTask>();
1290 RegionLocator regionLocator = null;
1291 try {
1292 regionLocator = admin.getConnection().getRegionLocator(tableDesc.getTableName());
1293 for (HRegionLocation location : regionLocator.getAllRegionLocations()) {
1294 ServerName rs = location.getServerName();
1295 HRegionInfo region = location.getRegionInfo();
1296 tasks.add(new RegionTask(admin.getConnection(), region, rs, (RegionStdOutSink) sink, taskType, rawScanEnabled,
1297 rwLatency));
1298 }
1299 } finally {
1300 if (regionLocator != null) {
1301 regionLocator.close();
1302 }
1303 }
1304 return executor.invokeAll(tasks);
1305 }
1306
1307
1308 private static class ZookeeperMonitor extends Monitor {
1309 private List<String> hosts;
1310 private final String znode;
1311 private final int timeout;
1312
1313 protected ZookeeperMonitor(Connection connection, String[] monitorTargets, boolean useRegExp,
1314 StdOutSink sink, ExecutorService executor, boolean treatFailureAsError, long allowedFailures) {
1315 super(connection, monitorTargets, useRegExp, sink, executor, treatFailureAsError, allowedFailures);
1316 Configuration configuration = connection.getConfiguration();
1317 znode =
1318 configuration.get(ZOOKEEPER_ZNODE_PARENT,
1319 DEFAULT_ZOOKEEPER_ZNODE_PARENT);
1320 timeout = configuration
1321 .getInt(HConstants.ZK_SESSION_TIMEOUT, HConstants.DEFAULT_ZK_SESSION_TIMEOUT);
1322 ConnectStringParser parser =
1323 new ConnectStringParser(ZKConfig.getZKQuorumServersString(configuration));
1324 hosts = Lists.newArrayList();
1325 for (InetSocketAddress server : parser.getServerAddresses()) {
1326 hosts.add(server.toString());
1327 }
1328 if (allowedFailures > (hosts.size() - 1) / 2) {
1329 LOG.warn(String.format("Confirm allowable number of failed ZooKeeper nodes, as quorum will " +
1330 "already be lost. Setting of %d failures is unexpected for %d ensemble size.",
1331 allowedFailures, hosts.size()));
1332 }
1333 }
1334
1335 @Override public void run() {
1336 List<ZookeeperTask> tasks = Lists.newArrayList();
1337 ZookeeperStdOutSink zkSink = null;
1338 try {
1339 zkSink = this.getSink();
1340 } catch (RuntimeException e) {
1341 LOG.error("Run ZooKeeperMonitor failed!", e);
1342 this.errorCode = ERROR_EXIT_CODE;
1343 }
1344 this.initialized = true;
1345 for (final String host : hosts) {
1346 tasks.add(new ZookeeperTask(connection, host, znode, timeout, zkSink));
1347 }
1348 try {
1349 for (Future<Void> future : this.executor.invokeAll(tasks)) {
1350 try {
1351 future.get();
1352 } catch (ExecutionException e) {
1353 LOG.error("Sniff zookeeper failed!", e);
1354 this.errorCode = ERROR_EXIT_CODE;
1355 }
1356 }
1357 } catch (InterruptedException e) {
1358 this.errorCode = ERROR_EXIT_CODE;
1359 Thread.currentThread().interrupt();
1360 LOG.error("Sniff zookeeper interrupted!", e);
1361 }
1362 this.done = true;
1363 }
1364
1365 private ZookeeperStdOutSink getSink() {
1366 if (!(sink instanceof ZookeeperStdOutSink)) {
1367 throw new RuntimeException("Can only write to zookeeper sink");
1368 }
1369 return ((ZookeeperStdOutSink) sink);
1370 }
1371 }
1372
1373
1374
1375 private static class RegionServerMonitor extends Monitor {
1376
1377 private boolean allRegions;
1378
1379 public RegionServerMonitor(Connection connection, String[] monitorTargets, boolean useRegExp,
1380 StdOutSink sink, ExecutorService executor, boolean allRegions,
1381 boolean treatFailureAsError, long allowedFailures) {
1382 super(connection, monitorTargets, useRegExp, sink, executor, treatFailureAsError, allowedFailures);
1383 this.allRegions = allRegions;
1384 }
1385
1386 private RegionServerStdOutSink getSink() {
1387 if (!(sink instanceof RegionServerStdOutSink)) {
1388 throw new RuntimeException("Can only write to regionserver sink");
1389 }
1390 return ((RegionServerStdOutSink) sink);
1391 }
1392
1393 @Override
1394 public void run() {
1395 if (this.initAdmin() && this.checkNoTableNames()) {
1396 RegionServerStdOutSink regionServerSink = null;
1397 try {
1398 regionServerSink = this.getSink();
1399 } catch (RuntimeException e) {
1400 LOG.error("Run RegionServerMonitor failed!", e);
1401 this.errorCode = ERROR_EXIT_CODE;
1402 }
1403 Map<String, List<HRegionInfo>> rsAndRMap = this.filterRegionServerByName();
1404 this.initialized = true;
1405 this.monitorRegionServers(rsAndRMap, regionServerSink);
1406 }
1407 this.done = true;
1408 }
1409
1410 private boolean checkNoTableNames() {
1411 List<String> foundTableNames = new ArrayList<String>();
1412 TableName[] tableNames = null;
1413
1414 if (LOG.isDebugEnabled()) {
1415 LOG.debug(String.format("reading list of tables"));
1416 }
1417 try {
1418 tableNames = this.admin.listTableNames();
1419 } catch (IOException e) {
1420 LOG.error("Get listTableNames failed", e);
1421 this.errorCode = INIT_ERROR_EXIT_CODE;
1422 return false;
1423 }
1424
1425 if (this.targets == null || this.targets.length == 0) return true;
1426
1427 for (String target : this.targets) {
1428 for (TableName tableName : tableNames) {
1429 if (target.equals(tableName.getNameAsString())) {
1430 foundTableNames.add(target);
1431 }
1432 }
1433 }
1434
1435 if (foundTableNames.size() > 0) {
1436 System.err.println("Cannot pass a tablename when using the -regionserver " +
1437 "option, tablenames:" + foundTableNames.toString());
1438 this.errorCode = USAGE_EXIT_CODE;
1439 }
1440 return foundTableNames.size() == 0;
1441 }
1442
1443 private void monitorRegionServers(Map<String, List<HRegionInfo>> rsAndRMap,
1444 RegionServerStdOutSink regionServerSink) {
1445 List<RegionServerTask> tasks = new ArrayList<>();
1446 Map<String, AtomicLong> successMap = new HashMap<>();
1447 Random rand = new Random();
1448 for (Map.Entry<String, List<HRegionInfo>> entry : rsAndRMap.entrySet()) {
1449 String serverName = entry.getKey();
1450 AtomicLong successes = new AtomicLong(0);
1451 successMap.put(serverName, successes);
1452 if (entry.getValue().isEmpty()) {
1453 LOG.error(String.format("Regionserver not serving any regions - %s", serverName));
1454 } else if (this.allRegions) {
1455 for (HRegionInfo region : entry.getValue()) {
1456 tasks.add(new RegionServerTask(this.connection,
1457 serverName,
1458 region,
1459 regionServerSink,
1460 successes));
1461 }
1462 } else {
1463
1464 HRegionInfo region = entry.getValue().get(rand.nextInt(entry.getValue().size()));
1465 tasks.add(new RegionServerTask(this.connection,
1466 serverName,
1467 region,
1468 regionServerSink,
1469 successes));
1470 }
1471 }
1472 try {
1473 for (Future<Void> future : this.executor.invokeAll(tasks)) {
1474 try {
1475 future.get();
1476 } catch (ExecutionException e) {
1477 LOG.error("Sniff regionserver failed!", e);
1478 this.errorCode = ERROR_EXIT_CODE;
1479 }
1480 }
1481 if (this.allRegions) {
1482 for (Map.Entry<String, List<HRegionInfo>> entry : rsAndRMap.entrySet()) {
1483 String serverName = entry.getKey();
1484 LOG.info("Successfully read " + successMap.get(serverName) + " regions out of "
1485 + entry.getValue().size() + " on regionserver:" + serverName);
1486 }
1487 }
1488 } catch (InterruptedException e) {
1489 this.errorCode = ERROR_EXIT_CODE;
1490 LOG.error("Sniff regionserver interrupted!", e);
1491 }
1492 }
1493
1494 private Map<String, List<HRegionInfo>> filterRegionServerByName() {
1495 Map<String, List<HRegionInfo>> regionServerAndRegionsMap = this.getAllRegionServerByName();
1496 regionServerAndRegionsMap = this.doFilterRegionServerByName(regionServerAndRegionsMap);
1497 return regionServerAndRegionsMap;
1498 }
1499
1500 private Map<String, List<HRegionInfo>> getAllRegionServerByName() {
1501 Map<String, List<HRegionInfo>> rsAndRMap = new HashMap<String, List<HRegionInfo>>();
1502 Table table = null;
1503 RegionLocator regionLocator = null;
1504 try {
1505 if (LOG.isDebugEnabled()) {
1506 LOG.debug(String.format("reading list of tables and locations"));
1507 }
1508 HTableDescriptor[] tableDescs = this.admin.listTables();
1509 List<HRegionInfo> regions = null;
1510 for (HTableDescriptor tableDesc : tableDescs) {
1511 table = this.admin.getConnection().getTable(tableDesc.getTableName());
1512 regionLocator = this.admin.getConnection().getRegionLocator(tableDesc.getTableName());
1513
1514 for (HRegionLocation location : regionLocator.getAllRegionLocations()) {
1515 ServerName rs = location.getServerName();
1516 String rsName = rs.getHostname();
1517 HRegionInfo r = location.getRegionInfo();
1518
1519 if (rsAndRMap.containsKey(rsName)) {
1520 regions = rsAndRMap.get(rsName);
1521 } else {
1522 regions = new ArrayList<HRegionInfo>();
1523 rsAndRMap.put(rsName, regions);
1524 }
1525 regions.add(r);
1526 }
1527 table.close();
1528 }
1529
1530
1531 for (ServerName rs : this.admin.getClusterStatus().getServers()) {
1532 String rsName = rs.getHostname();
1533 if (!rsAndRMap.containsKey(rsName)) {
1534 rsAndRMap.put(rsName, Collections.<HRegionInfo>emptyList());
1535 }
1536 }
1537 } catch (IOException e) {
1538 String msg = "Get HTables info failed";
1539 LOG.error(msg, e);
1540 this.errorCode = INIT_ERROR_EXIT_CODE;
1541 } finally {
1542 if (table != null) {
1543 try {
1544 table.close();
1545 } catch (IOException e) {
1546 LOG.warn("Close table failed", e);
1547 }
1548 }
1549 }
1550
1551 return rsAndRMap;
1552 }
1553
1554 private Map<String, List<HRegionInfo>> doFilterRegionServerByName(
1555 Map<String, List<HRegionInfo>> fullRsAndRMap) {
1556
1557 Map<String, List<HRegionInfo>> filteredRsAndRMap = null;
1558
1559 if (this.targets != null && this.targets.length > 0) {
1560 filteredRsAndRMap = new HashMap<String, List<HRegionInfo>>();
1561 Pattern pattern = null;
1562 Matcher matcher = null;
1563 boolean regExpFound = false;
1564 for (String rsName : this.targets) {
1565 if (this.useRegExp) {
1566 regExpFound = false;
1567 pattern = Pattern.compile(rsName);
1568 for (Map.Entry<String, List<HRegionInfo>> entry : fullRsAndRMap.entrySet()) {
1569 matcher = pattern.matcher(entry.getKey());
1570 if (matcher.matches()) {
1571 filteredRsAndRMap.put(entry.getKey(), entry.getValue());
1572 regExpFound = true;
1573 }
1574 }
1575 if (!regExpFound) {
1576 LOG.info("No RegionServerInfo found, regionServerPattern:" + rsName);
1577 }
1578 } else {
1579 if (fullRsAndRMap.containsKey(rsName)) {
1580 filteredRsAndRMap.put(rsName, fullRsAndRMap.get(rsName));
1581 } else {
1582 LOG.info("No RegionServerInfo found, regionServerName:" + rsName);
1583 }
1584 }
1585 }
1586 } else {
1587 filteredRsAndRMap = fullRsAndRMap;
1588 }
1589 return filteredRsAndRMap;
1590 }
1591 }
1592
1593 public static void main(String[] args) throws Exception {
1594 final Configuration conf = HBaseConfiguration.create();
1595
1596
1597 new GenericOptionsParser(conf, args);
1598
1599 int numThreads = conf.getInt("hbase.canary.threads.num", MAX_THREADS_NUM);
1600 LOG.info("Number of execution threads " + numThreads);
1601
1602 ExecutorService executor = new ScheduledThreadPoolExecutor(numThreads);
1603
1604 Class<? extends Sink> sinkClass =
1605 conf.getClass("hbase.canary.sink.class", RegionServerStdOutSink.class, Sink.class);
1606 Sink sink = ReflectionUtils.newInstance(sinkClass);
1607
1608 int exitCode = ToolRunner.run(conf, new Canary(executor, sink), args);
1609 executor.shutdown();
1610 System.exit(exitCode);
1611 }
1612 }