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 java.io.IOException;
44  import java.util.*;
45  
46  /**
47   * Provides information about the existing states of replication, replication peers and queues.
48   *
49   * Usage: hbase org.apache.hadoop.hbase.replication.regionserver.DumpReplicationQueues [args]
50   * Arguments: --distributed    Polls each RS to dump information about the queue
51   *            --hdfs           Reports HDFS usage by the replication queues (note: can be overestimated).
52   */
53  
54  public class DumpReplicationQueues extends Configured implements Tool {
55  
56    private static final Log LOG = LogFactory.getLog(DumpReplicationQueues.class.getName());
57  
58    static class DumpOptions {
59      boolean hdfs = false;
60      boolean distributed = false;
61  
62      public DumpOptions() {
63      }
64  
65      public DumpOptions(DumpOptions that) {
66        this.hdfs = that.hdfs;
67        this.distributed = that.distributed;
68      }
69  
70      boolean isHdfs () {
71        return hdfs;
72      }
73  
74      boolean isDistributed() {
75        return distributed;
76      }
77  
78      void setHdfs (boolean hdfs) {
79        this.hdfs = hdfs;
80      }
81  
82      void setDistributed(boolean distributed) {
83        this.distributed = distributed;
84      }
85    }
86  
87    static DumpOptions parseOpts(Queue<String> args) {
88      DumpOptions opts = new DumpOptions();
89  
90      String cmd = null;
91      while ((cmd = args.poll()) != null) {
92        if (cmd.equals("-h") || cmd.equals("--h") || cmd.equals("--help")) {
93          // place item back onto queue so that caller knows parsing was incomplete
94          args.add(cmd);
95          break;
96        }
97        final String hdfs = "--hdfs";
98        if (cmd.equals(hdfs)) {
99          opts.setHdfs(true);
100         continue;
101       }
102       final String distributed = "--distributed";
103       if (cmd.equals(distributed)) {
104         opts.setDistributed(true);
105         continue;
106       } else {
107         printUsageAndExit("ERROR: Unrecognized option/command: " + cmd, -1);
108       }
109       // check that --distributed is present when --hdfs is in the arguments
110       if (!opts.isDistributed()  && opts.isHdfs()) {
111         printUsageAndExit("ERROR: --hdfs option can only be used with --distributed: " + cmd, -1);
112       }
113     }
114     return opts;
115   }
116 
117   /**
118    * Main
119    *
120    * @param args
121    * @throws Exception
122    */
123   public static void main(String[] args) throws Exception {
124     Configuration conf = HBaseConfiguration.create();
125     int ret = ToolRunner.run(conf, new DumpReplicationQueues(), args);
126     System.exit(ret);
127   }
128 
129   @Override
130   public int run(String[] args) throws Exception {
131 
132     int errCode = -1;
133     LinkedList<String> argv = new LinkedList<String>();
134     argv.addAll(Arrays.asList(args));
135     DumpOptions opts = parseOpts(argv);
136 
137     // args remaining, print help and exit
138     if (!argv.isEmpty()) {
139       errCode = 0;
140       printUsage();
141       return errCode;
142     }
143     return dumpReplicationQueues(opts);
144   }
145 
146   protected void printUsage() {
147     printUsage(this.getClass().getName(), null);
148   }
149 
150   protected static void printUsage(final String message) {
151     printUsage(DumpReplicationQueues.class.getName(), message);
152   }
153 
154   protected static void printUsage(final String className, final String message) {
155     if (message != null && message.length() > 0) {
156       System.err.println(message);
157     }
158     System.err.println("Usage: java " + className + " \\");
159     System.err.println("  <OPTIONS> [-D<property=value>]*");
160     System.err.println();
161     System.err.println("General Options:");
162     System.err.println(" distributed  Poll each RS and print its own replication queue. "
163         + "Default only polls ZooKeeper");
164     System.err.println(" hdfs         Use HDFS to calculate usage of WALs by replication. It could be overestimated"
165         + " if replicating to multiple peers. --distributed flag is also needed.");
166   }
167 
168   protected static void printUsageAndExit(final String message, final int exitCode) {
169     printUsage(message);
170     System.exit(exitCode);
171   }
172 
173   private int dumpReplicationQueues(DumpOptions opts) throws Exception {
174 
175     Configuration conf = getConf();
176     HBaseAdmin.checkHBaseAvailable(conf);
177     ReplicationAdmin replicationAdmin = new ReplicationAdmin(conf);
178     ClusterConnection connection = (ClusterConnection) ConnectionFactory.createConnection(conf);
179     long deleted = 0;
180 
181     ZooKeeperWatcher zkw = new ZooKeeperWatcher(conf, "DumpReplicationQueues" + System.currentTimeMillis(),
182         new WarnOnlyAbortable(), true);
183 
184     try {
185       // Our zk watcher
186       LOG.info("Our Quorum: " + zkw.getQuorum());
187       List<HashMap<String, String>> replicatedTables = replicationAdmin.listReplicated();
188       if (replicatedTables.isEmpty()) {
189         LOG.info("No tables with a configured replication peer were found.");
190         return(0);
191       } else {
192         LOG.info("Replicated Tables: " + replicatedTables);
193       }
194 
195       Map<String, ReplicationPeerConfig> peerConfigs = replicationAdmin.listPeerConfigs();
196 
197       if (peerConfigs.isEmpty()) {
198         LOG.info("Replication is enabled but no peer configuration was found.");
199       }
200 
201       System.out.println("Dumping replication peers and configurations:");
202       System.out.println(dumpPeersState(replicationAdmin, peerConfigs));
203 
204       if (opts.isDistributed()) {
205         LOG.info("Found [--distributed], will poll each RegionServer.");
206         System.out.println(dumpQueues(connection, zkw, opts.isHdfs(), deleted));
207         if (deleted > 0) {
208           LOG.warn("Found " + deleted +" deleted queues"
209               + ", run hbck -fixReplication in order to remove the deleted replication queues");
210         }
211       } else {
212         // use ZK instead
213         System.out.print("Dumping replication znodes via ZooKeeper:");
214         System.out.println(ZKUtil.getReplicationZnodesDump(zkw));
215       }
216       return (0);
217     } catch (IOException e) {
218       return (-1);
219     } finally {
220       zkw.close();
221     }
222   }
223 
224   public String dumpPeersState(ReplicationAdmin replicationAdmin,
225                               Map<String, ReplicationPeerConfig> peerConfigs) throws Exception {
226     Map<String, String> currentConf;
227     StringBuilder sb = new StringBuilder();
228     for (Map.Entry<String, ReplicationPeerConfig> peer : peerConfigs.entrySet()) {
229       try {
230         sb.append("Peer: " + peer.getKey() + "\n");
231         sb.append("    " + "State: " + (replicationAdmin.getPeerState(peer.getKey()) ? "ENABLED" : "DISABLED") + "\n");
232         sb.append("    " + "Cluster Name: " + peer.getValue() + "\n");
233         currentConf = peer.getValue().getConfiguration();
234         // Only show when we have a custom configuration for the peer
235         if (currentConf.size() > 1) {
236           sb.append("    " + "Peer Configuration: " + currentConf + "\n");
237         }
238         sb.append("    " + "Peer Table CFs: " + replicationAdmin.getPeerTableCFs(peer.getKey()) + "\n");
239       } catch (ReplicationException re) {
240         sb.append("Got an exception while invoking ReplicationAdmin: " + re + "\n");
241       }
242     }
243     return sb.toString();
244   }
245 
246   public String dumpQueues(ClusterConnection connection, ZooKeeperWatcher zkw, boolean hdfs, long deleted)
247       throws Exception {
248     ReplicationQueuesClient queuesClient;
249     ReplicationPeers replicationPeers;
250     ReplicationQueues replicationQueues;
251 
252     StringBuilder sb = new StringBuilder();
253 
254     queuesClient = ReplicationFactory.getReplicationQueuesClient(zkw, getConf(), connection);
255     queuesClient.init();
256     replicationQueues = ReplicationFactory.getReplicationQueues(zkw, getConf(), connection);
257     replicationPeers = ReplicationFactory.getReplicationPeers(zkw, getConf(), queuesClient, connection);
258     replicationPeers.init();
259 
260     // Loops each peer on each RS and dumps the queues
261 
262     Set<String> peerIds = new HashSet<String>(replicationPeers.getAllPeerIds());
263     try {
264       List<String> regionservers = queuesClient.getListOfReplicators();
265       for (String regionserver : regionservers) {
266         List<String> queueIds = queuesClient.getAllQueues(regionserver);
267         replicationQueues.init(regionserver);
268         for (String queueId : queueIds) {
269           ReplicationQueueInfo queueInfo = new ReplicationQueueInfo(queueId);
270           List<String> wals = queuesClient.getLogsInQueue(regionserver, queueId);
271           if (!peerIds.contains(queueInfo.getPeerId())) {
272             deleted++;
273             sb.append(formatQueue(regionserver, replicationQueues, queueInfo, queueId, wals, true, hdfs));
274           } else {
275             sb.append(formatQueue(regionserver, replicationQueues, queueInfo, queueId, wals, false, hdfs));
276           }
277         }
278       }
279     } catch (KeeperException ke) {
280       throw new IOException(ke);
281     }
282     return sb.toString();
283   }
284 
285   private String formatQueue(String regionserver, ReplicationQueues replicationQueues, ReplicationQueueInfo queueInfo,
286                            String queueId, List<String> wals, boolean isDeleted, boolean hdfs) throws Exception {
287 
288     StringBuilder sb = new StringBuilder();
289 
290     List<String> deadServers ;
291 
292     sb.append("Dumping replication queue info for RegionServer: [" + regionserver + "]" + "\n");
293     sb.append("    PeerID: " + queueInfo.getPeerId() + "\n");
294     sb.append("    Recovered: " + queueInfo.isQueueRecovered() + "\n");
295     deadServers = queueInfo.getDeadRegionServers();
296     if (deadServers.isEmpty()) {
297       sb.append("    No dead RegionServers found in this queue." + "\n");
298     } else {
299       sb.append("    Dead RegionServers: " + deadServers + "\n");
300     }
301     sb.append("    Was deleted: " + isDeleted + "\n");
302     sb.append("    Number of WALs in replication queue: " + wals.size() + "\n");
303     for (String wal : wals) {
304       long position = replicationQueues.getLogPosition(queueInfo.getPeerId(), wal);
305       sb.append("    Replication position for " + wal + ": " + (position > 0 ? position : "0"
306           + " (not started or nothing to replicate)") + "\n");
307     }
308 
309     if (hdfs) {
310       FileSystem fs = FileSystem.get(getConf());
311       sb.append("    Total size of WALs on HDFS for this queue: "
312           + StringUtils.humanSize(getTotalWALSize(fs, wals, regionserver)) + "\n");
313     }
314     return sb.toString();
315   }
316   /**
317    *  return total size in bytes from a list of WALs
318    */
319   private long getTotalWALSize(FileSystem fs, List<String> wals, String server) throws IOException {
320     int size = 0;
321     FileStatus fileStatus;
322 
323     for (String wal : wals) {
324       try {
325         fileStatus = (new WALLink(getConf(), server, wal)).getFileStatus(fs);
326       } catch (IOException e) {
327         LOG.warn("WAL " + wal + " couldn't be found, skipping");
328         break;
329       }
330       size += fileStatus.getLen();
331     }
332     return size;
333   }
334 
335   private static class WarnOnlyAbortable implements Abortable {
336     @Override
337     public void abort(String why, Throwable e) {
338       LOG.warn("DumpReplicationQueue received abort, ignoring.  Reason: " + why);
339       if (LOG.isDebugEnabled()) {
340         LOG.debug(e);
341       }
342     }
343 
344     @Override
345     public boolean isAborted() {
346       return false;
347     }
348   }
349 
350   private static void usage(final String errorMsg) {
351     if (errorMsg != null && errorMsg.length() > 0) {
352       LOG.error(errorMsg);
353     }
354   }
355 }