1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
51
52
53
54
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
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
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
136
137
138
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
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
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
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
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
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
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 }