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 java.io.IOException;
44 import java.util.*;
45
46
47
48
49
50
51
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
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
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
119
120
121
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
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
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
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
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
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
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 }