View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.replication.regionserver;
19  
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  import org.apache.hadoop.conf.Configuration;
23  import org.apache.hadoop.conf.Configured;
24  import org.apache.hadoop.fs.FileStatus;
25  import org.apache.hadoop.fs.FileSystem;
26  import org.apache.hadoop.fs.Path;
27  import org.apache.hadoop.hbase.*;
28  import org.apache.hadoop.hbase.client.ClusterConnection;
29  import org.apache.hadoop.hbase.client.ConnectionFactory;
30  import org.apache.hadoop.hbase.client.HBaseAdmin;
31  import org.apache.hadoop.hbase.client.replication.ReplicationAdmin;
32  import org.apache.hadoop.hbase.io.WALLink;
33  import org.apache.hadoop.hbase.procedure2.util.StringUtils;
34  import org.apache.hadoop.hbase.replication.*;
35  import org.apache.hadoop.hbase.util.FSUtils;
36  import org.apache.hadoop.hbase.zookeeper.ZKUtil;
37  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
38  import org.apache.hadoop.util.Tool;
39  import org.apache.hadoop.util.ToolRunner;
40  import org.apache.zookeeper.KeeperException;
41  import org.mortbay.util.IO;
42  
43  import com.google.common.util.concurrent.AtomicLongMap;
44  
45  import java.io.FileNotFoundException;
46  import java.io.IOException;
47  import java.util.*;
48  
49  /**
50   * Provides information about the existing states of replication, replication peers and queues.
51   *
52   * Usage: hbase org.apache.hadoop.hbase.replication.regionserver.DumpReplicationQueues [args]
53   * Arguments: --distributed    Polls each RS to dump information about the queue
54   *            --hdfs           Reports HDFS usage by the replication queues (note: can be overestimated).
55   */
56  
57  public class DumpReplicationQueues extends Configured implements Tool {
58  
59    private static final Log LOG = LogFactory.getLog(DumpReplicationQueues.class.getName());
60  
61    private List<String> deadRegionServers;
62    private List<String> deletedQueues;
63    private AtomicLongMap<String> peersQueueSize;
64    private long totalSizeOfWALs;
65    private long numWalsNotFound;
66  
67    public DumpReplicationQueues() {
68      deadRegionServers = new ArrayList<String>();
69      deletedQueues = new ArrayList<String>();
70      peersQueueSize = AtomicLongMap.create();
71      totalSizeOfWALs = 0;
72      numWalsNotFound = 0;
73    }
74  
75    static class DumpOptions {
76      boolean hdfs = false;
77      boolean distributed = false;
78  
79      public DumpOptions() {
80      }
81  
82      public DumpOptions(DumpOptions that) {
83        this.hdfs = that.hdfs;
84        this.distributed = that.distributed;
85      }
86  
87      boolean isHdfs () {
88        return hdfs;
89      }
90  
91      boolean isDistributed() {
92        return distributed;
93      }
94  
95      void setHdfs (boolean hdfs) {
96        this.hdfs = hdfs;
97      }
98  
99      void setDistributed(boolean distributed) {
100       this.distributed = distributed;
101     }
102   }
103 
104   static DumpOptions parseOpts(Queue<String> args) {
105     DumpOptions opts = new DumpOptions();
106 
107     String cmd = null;
108     while ((cmd = args.poll()) != null) {
109       if (cmd.equals("-h") || cmd.equals("--h") || cmd.equals("--help")) {
110         // place item back onto queue so that caller knows parsing was incomplete
111         args.add(cmd);
112         break;
113       }
114       final String hdfs = "--hdfs";
115       if (cmd.equals(hdfs)) {
116         opts.setHdfs(true);
117         continue;
118       }
119       final String distributed = "--distributed";
120       if (cmd.equals(distributed)) {
121         opts.setDistributed(true);
122         continue;
123       } else {
124         printUsageAndExit("ERROR: Unrecognized option/command: " + cmd, -1);
125       }
126       // check that --distributed is present when --hdfs is in the arguments
127       if (!opts.isDistributed()  && opts.isHdfs()) {
128         printUsageAndExit("ERROR: --hdfs option can only be used with --distributed: " + cmd, -1);
129       }
130     }
131     return opts;
132   }
133 
134   /**
135    * Main
136    *
137    * @param args
138    * @throws Exception
139    */
140   public static void main(String[] args) throws Exception {
141     Configuration conf = HBaseConfiguration.create();
142     int ret = ToolRunner.run(conf, new DumpReplicationQueues(), args);
143     System.exit(ret);
144   }
145 
146   @Override
147   public int run(String[] args) throws Exception {
148 
149     int errCode = -1;
150     LinkedList<String> argv = new LinkedList<String>();
151     argv.addAll(Arrays.asList(args));
152     DumpOptions opts = parseOpts(argv);
153 
154     // args remaining, print help and exit
155     if (!argv.isEmpty()) {
156       errCode = 0;
157       printUsage();
158       return errCode;
159     }
160     return dumpReplicationQueues(opts);
161   }
162 
163   protected void printUsage() {
164     printUsage(this.getClass().getName(), null);
165   }
166 
167   protected static void printUsage(final String message) {
168     printUsage(DumpReplicationQueues.class.getName(), message);
169   }
170 
171   protected static void printUsage(final String className, final String message) {
172     if (message != null && message.length() > 0) {
173       System.err.println(message);
174     }
175     System.err.println("Usage: bin/hbase " + className + " \\");
176     System.err.println("  <OPTIONS> [-D<property=value>]*");
177     System.err.println();
178     System.err.println("General Options:");
179     System.err.println(" -h|--h|--help  Show this help and exit.");
180     System.err.println(" --distributed  Poll each RS and print its own replication queue. "
181         + "Default only polls ZooKeeper");
182     System.err.println(" --hdfs         Use HDFS to calculate usage of WALs by replication. It could be overestimated"
183         + " if replicating to multiple peers. --distributed flag is also needed.");
184   }
185 
186   protected static void printUsageAndExit(final String message, final int exitCode) {
187     printUsage(message);
188     System.exit(exitCode);
189   }
190 
191   private int dumpReplicationQueues(DumpOptions opts) throws Exception {
192 
193     Configuration conf = getConf();
194     HBaseAdmin.checkHBaseAvailable(conf);
195     ReplicationAdmin replicationAdmin = new ReplicationAdmin(conf);
196     ClusterConnection connection = (ClusterConnection) ConnectionFactory.createConnection(conf);
197 
198     ZooKeeperWatcher zkw = new ZooKeeperWatcher(conf, "DumpReplicationQueues" + System.currentTimeMillis(),
199         new WarnOnlyAbortable(), true);
200 
201     try {
202       // Our zk watcher
203       LOG.info("Our Quorum: " + zkw.getQuorum());
204       List<HashMap<String, String>> replicatedTables = replicationAdmin.listReplicated();
205       if (replicatedTables.isEmpty()) {
206         LOG.info("No tables with a configured replication peer were found.");
207         return(0);
208       } else {
209         LOG.info("Replicated Tables: " + replicatedTables);
210       }
211 
212       Map<String, ReplicationPeerConfig> peerConfigs = replicationAdmin.listPeerConfigs();
213 
214       if (peerConfigs.isEmpty()) {
215         LOG.info("Replication is enabled but no peer configuration was found.");
216       }
217 
218       System.out.println("Dumping replication peers and configurations:");
219       System.out.println(dumpPeersState(replicationAdmin, peerConfigs));
220 
221       if (opts.isDistributed()) {
222         LOG.info("Found [--distributed], will poll each RegionServer.");
223         System.out.println(dumpQueues(connection, peerConfigs.keySet(), zkw, opts.isHdfs()));
224         System.out.println(dumpReplicationSummary());
225       } else {
226         // use ZK instead
227         System.out.print("Dumping replication znodes via ZooKeeper:");
228         System.out.println(ZKUtil.getReplicationZnodesDump(zkw));
229       }
230       return (0);
231     } catch (IOException e) {
232       return (-1);
233     } finally {
234       zkw.close();
235     }
236   }
237 
238   public String dumpReplicationSummary() {
239     StringBuilder sb = new StringBuilder();
240     if (!deletedQueues.isEmpty()) {
241       sb.append("Found " + deletedQueues.size() + " deleted queues"
242           + ", run hbck -fixReplication in order to remove the deleted replication queues\n");
243       for (String deletedQueue : deletedQueues) {
244         sb.append("    " + deletedQueue + "\n");
245       }
246     }
247     if (!deadRegionServers.isEmpty()) {
248       sb.append("Found " + deadRegionServers.size() + " dead regionservers"
249           + ", restart one regionserver to transfer the queues of dead regionservers\n");
250       for (String deadRs : deadRegionServers) {
251         sb.append("    " + deadRs + "\n");
252       }
253     }
254     if (!peersQueueSize.isEmpty()) {
255       sb.append("Dumping all peers's number of WALs in replication queue\n");
256       for (Map.Entry<String, Long> entry : peersQueueSize.asMap().entrySet()) {
257         sb.append("    PeerId: " + entry.getKey() + " , sizeOfLogQueue: " + entry.getValue() + "\n");
258       }
259     }
260     sb.append("    Total size of WALs on HDFS: " + StringUtils.humanSize(totalSizeOfWALs) + "\n");
261     if (numWalsNotFound > 0) {
262       sb.append("    ERROR: There are " + numWalsNotFound + " WALs not found!!!\n");
263     }
264     return sb.toString();
265   }
266 
267   public String dumpPeersState(ReplicationAdmin replicationAdmin,
268       Map<String, ReplicationPeerConfig> peerConfigs) throws Exception {
269     Map<String, String> currentConf;
270     StringBuilder sb = new StringBuilder();
271     for (Map.Entry<String, ReplicationPeerConfig> peer : peerConfigs.entrySet()) {
272       try {
273         ReplicationPeerConfig peerConfig = peer.getValue();
274         sb.append("Peer: " + peer.getKey() + "\n");
275         sb.append("    " + "State: "
276             + (replicationAdmin.getPeerState(peer.getKey()) ? "ENABLED" : "DISABLED") + "\n");
277         sb.append("    " + "Cluster Name: " + peerConfig.getClusterKey() + "\n");
278         currentConf = peerConfig.getConfiguration();
279         // Only show when we have a custom configuration for the peer
280         if (currentConf.size() > 1) {
281           sb.append("    " + "Peer Configuration: " + currentConf + "\n");
282         }
283         sb.append("    " + "Peer Table CFs: " + peerConfig.getTableCFsMap() + "\n");
284       } catch (ReplicationException re) {
285         sb.append("Got an exception while invoking ReplicationAdmin: " + re + "\n");
286       }
287     }
288     return sb.toString();
289   }
290 
291   public String dumpQueues(ClusterConnection connection, Set<String> peerIds, ZooKeeperWatcher zkw,
292       boolean hdfs) throws Exception {
293     ReplicationQueuesClient queuesClient;
294     ReplicationPeers replicationPeers;
295     ReplicationQueues replicationQueues;
296     ReplicationTracker replicationTracker;
297     StringBuilder sb = new StringBuilder();
298 
299     queuesClient = ReplicationFactory.getReplicationQueuesClient(zkw, getConf(), connection);
300     queuesClient.init();
301     replicationQueues = ReplicationFactory.getReplicationQueues(zkw, getConf(), connection);
302     replicationPeers = ReplicationFactory.getReplicationPeers(zkw, getConf(), queuesClient, connection);
303     replicationTracker = ReplicationFactory.getReplicationTracker(zkw, replicationPeers, getConf(),
304       new WarnOnlyAbortable(), new WarnOnlyStoppable());
305     List<String> liveRegionServers = replicationTracker.getListOfRegionServers();
306 
307     // Loops each peer on each RS and dumps the queues
308     try {
309       List<String> regionservers = queuesClient.getListOfReplicators();
310       if (regionservers == null || regionservers.isEmpty()) {
311         return sb.toString();
312       }
313       for (String regionserver : regionservers) {
314         List<String> queueIds = queuesClient.getAllQueues(regionserver);
315         replicationQueues.init(regionserver);
316         if (!liveRegionServers.contains(regionserver)) {
317           deadRegionServers.add(regionserver);
318         }
319         for (String queueId : queueIds) {
320           ReplicationQueueInfo queueInfo = new ReplicationQueueInfo(queueId);
321           List<String> wals = queuesClient.getLogsInQueue(regionserver, queueId);
322           if (!peerIds.contains(queueInfo.getPeerId())) {
323             deletedQueues.add(regionserver + "/" + queueId);
324             sb.append(formatQueue(regionserver, replicationQueues, queueInfo, queueId, wals, true, hdfs));
325           } else {
326             sb.append(formatQueue(regionserver, replicationQueues, queueInfo, queueId, wals, false, hdfs));
327           }
328         }
329       }
330     } catch (KeeperException ke) {
331       throw new IOException(ke);
332     }
333     return sb.toString();
334   }
335 
336   private String formatQueue(String regionserver, ReplicationQueues replicationQueues,
337       ReplicationQueueInfo queueInfo, String queueId, List<String> wals, boolean isDeleted,
338       boolean hdfs) throws Exception {
339     StringBuilder sb = new StringBuilder();
340     List<String> deadServers ;
341 
342     sb.append("Dumping replication queue info for RegionServer: [" + regionserver + "]" + "\n");
343     sb.append("    Queue znode: " + queueId + "\n");
344     sb.append("    PeerID: " + queueInfo.getPeerId() + "\n");
345     sb.append("    Recovered: " + queueInfo.isQueueRecovered() + "\n");
346     deadServers = queueInfo.getDeadRegionServers();
347     if (deadServers.isEmpty()) {
348       sb.append("    No dead RegionServers found in this queue." + "\n");
349     } else {
350       sb.append("    Dead RegionServers: " + deadServers + "\n");
351     }
352     sb.append("    Was deleted: " + isDeleted + "\n");
353     sb.append("    Number of WALs in replication queue: " + wals.size() + "\n");
354     peersQueueSize.addAndGet(queueInfo.getPeerId(), wals.size());
355 
356     for (String wal : wals) {
357       long position = replicationQueues.getLogPosition(queueInfo.getPeerId(), wal);
358       sb.append("    Replication position for " + wal + ": " + (position > 0 ? position : "0"
359           + " (not started or nothing to replicate)") + "\n");
360     }
361 
362     if (hdfs) {
363       FileSystem fs = FileSystem.get(getConf());
364       sb.append("    Total size of WALs on HDFS for this queue: "
365           + StringUtils.humanSize(getTotalWALSize(fs, wals, regionserver)) + "\n");
366     }
367     return sb.toString();
368   }
369   /**
370    *  return total size in bytes from a list of WALs
371    */
372   private long getTotalWALSize(FileSystem fs, List<String> wals, String server) throws IOException {
373     long size = 0;
374     FileStatus fileStatus;
375 
376     for (String wal : wals) {
377       try {
378         fileStatus = (new WALLink(getConf(), server, wal)).getFileStatus(fs);
379       } catch (IOException e) {
380         if (e instanceof FileNotFoundException) {
381           numWalsNotFound++;
382           LOG.warn("WAL " + wal + " couldn't be found, skipping", e);
383         } else {
384           LOG.warn("Can't get file status of WAL " + wal + ", skipping", e);
385         }
386         continue;
387       }
388       size += fileStatus.getLen();
389     }
390 
391     totalSizeOfWALs += size;
392     return size;
393   }
394 
395   private static class WarnOnlyAbortable implements Abortable {
396     @Override
397     public void abort(String why, Throwable e) {
398       LOG.warn("DumpReplicationQueue received abort, ignoring.  Reason: " + why);
399       if (LOG.isDebugEnabled()) {
400         LOG.debug(e);
401       }
402     }
403 
404     @Override
405     public boolean isAborted() {
406       return false;
407     }
408   }
409 
410   private static class WarnOnlyStoppable implements Stoppable {
411     @Override
412     public void stop(String why) {
413       LOG.warn("DumpReplicationQueue received stop, ignoring.  Reason: " + why);
414     }
415 
416     @Override
417     public boolean isStopped() {
418       return false;
419     }
420   }
421 }