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