1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.replication;
20
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.SortedMap;
24 import java.util.SortedSet;
25 import java.util.TreeMap;
26 import java.util.TreeSet;
27
28 import com.google.common.annotations.VisibleForTesting;
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.hbase.classification.InterfaceAudience;
32 import org.apache.hadoop.conf.Configuration;
33 import org.apache.hadoop.hbase.Abortable;
34 import org.apache.hadoop.hbase.HConstants;
35 import org.apache.hadoop.hbase.exceptions.DeserializationException;
36 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
37 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
38 import org.apache.hadoop.hbase.util.Bytes;
39 import org.apache.hadoop.hbase.util.Pair;
40 import org.apache.hadoop.hbase.zookeeper.ZKUtil;
41 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
42 import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp;
43 import org.apache.zookeeper.KeeperException;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 @InterfaceAudience.Private
67 public class ReplicationQueuesZKImpl extends ReplicationStateZKBase implements ReplicationQueues {
68
69
70 private String myQueuesZnode;
71
72 public final static String RS_LOCK_ZNODE = "lock";
73
74 private static final Log LOG = LogFactory.getLog(ReplicationQueuesZKImpl.class);
75
76 public ReplicationQueuesZKImpl(final ZooKeeperWatcher zk, Configuration conf,
77 Abortable abortable) {
78 super(zk, conf, abortable);
79 }
80
81 @Override
82 public void init(String serverName) throws ReplicationException {
83 this.myQueuesZnode = ZKUtil.joinZNode(this.queuesZNode, serverName);
84 try {
85 if (ZKUtil.checkExists(this.zookeeper, this.myQueuesZnode) < 0) {
86 ZKUtil.createWithParents(this.zookeeper, this.myQueuesZnode);
87 }
88 } catch (KeeperException e) {
89 throw new ReplicationException("Could not initialize replication queues.", e);
90 }
91 if (conf.getBoolean(HConstants.REPLICATION_BULKLOAD_ENABLE_KEY,
92 HConstants.REPLICATION_BULKLOAD_ENABLE_DEFAULT)) {
93 try {
94 if (ZKUtil.checkExists(this.zookeeper, this.hfileRefsZNode) < 0) {
95 ZKUtil.createWithParents(this.zookeeper, this.hfileRefsZNode);
96 }
97 } catch (KeeperException e) {
98 throw new ReplicationException("Could not initialize hfile references replication queue.",
99 e);
100 }
101 }
102 }
103
104 @Override
105 public List<String> getListOfReplicators() throws ReplicationException {
106 try {
107 return super.getListOfReplicatorsZK();
108 } catch (KeeperException e) {
109 LOG.warn("getListOfReplicators() from ZK failed", e);
110 throw new ReplicationException("getListOfReplicators() from ZK failed", e);
111 }
112 }
113
114 @Override
115 public void removeQueue(String queueId) {
116 try {
117 ZKUtil.deleteNodeRecursively(this.zookeeper, ZKUtil.joinZNode(this.myQueuesZnode, queueId));
118 } catch (KeeperException e) {
119 this.abortable.abort("Failed to delete queue (queueId=" + queueId + ")", e);
120 }
121 }
122
123 @Override
124 public void addLog(String queueId, String filename) throws ReplicationException {
125 String znode = ZKUtil.joinZNode(this.myQueuesZnode, queueId);
126 znode = ZKUtil.joinZNode(znode, filename);
127 try {
128 ZKUtil.createWithParents(this.zookeeper, znode);
129 } catch (KeeperException e) {
130 throw new ReplicationException(
131 "Could not add log because znode could not be created. queueId=" + queueId
132 + ", filename=" + filename);
133 }
134 }
135
136 @Override
137 public void removeLog(String queueId, String filename) {
138 try {
139 String znode = ZKUtil.joinZNode(this.myQueuesZnode, queueId);
140 znode = ZKUtil.joinZNode(znode, filename);
141 ZKUtil.deleteNode(this.zookeeper, znode);
142 } catch (KeeperException e) {
143 this.abortable.abort("Failed to remove wal from queue (queueId=" + queueId + ", filename="
144 + filename + ")", e);
145 }
146 }
147
148 @Override
149 public void setLogPosition(String queueId, String filename, long position) {
150 try {
151 String znode = ZKUtil.joinZNode(this.myQueuesZnode, queueId);
152 znode = ZKUtil.joinZNode(znode, filename);
153
154 ZKUtil.setData(this.zookeeper, znode, ZKUtil.positionToByteArray(position));
155 } catch (KeeperException e) {
156 this.abortable.abort("Failed to write replication wal position (filename=" + filename
157 + ", position=" + position + ")", e);
158 }
159 }
160
161 @Override
162 public long getLogPosition(String queueId, String filename) throws ReplicationException {
163 String clusterZnode = ZKUtil.joinZNode(this.myQueuesZnode, queueId);
164 String znode = ZKUtil.joinZNode(clusterZnode, filename);
165 byte[] bytes = null;
166 try {
167 bytes = ZKUtil.getData(this.zookeeper, znode);
168 } catch (KeeperException e) {
169 throw new ReplicationException("Internal Error: could not get position in log for queueId="
170 + queueId + ", filename=" + filename, e);
171 } catch (InterruptedException e) {
172 Thread.currentThread().interrupt();
173 return 0;
174 }
175 try {
176 return ZKUtil.parseWALPositionFrom(bytes);
177 } catch (DeserializationException de) {
178 LOG.warn("Failed to parse WALPosition for queueId=" + queueId + " and wal=" + filename
179 + " znode content, continuing.");
180 }
181
182
183 return 0;
184 }
185
186 @Override
187 public boolean isThisOurZnode(String znode) {
188 return ZKUtil.joinZNode(this.queuesZNode, znode).equals(this.myQueuesZnode);
189 }
190
191 @Override
192 public void removeAllQueues() {
193 try {
194 ZKUtil.deleteNodeRecursively(this.zookeeper, this.myQueuesZnode);
195 } catch (KeeperException e) {
196
197 if (e instanceof KeeperException.SessionExpiredException) {
198 return;
199 }
200 this.abortable.abort("Failed to delete replication queues for region server: "
201 + this.myQueuesZnode, e);
202 }
203 }
204
205 @Override
206 public List<String> getLogsInQueue(String queueId) {
207 String znode = ZKUtil.joinZNode(this.myQueuesZnode, queueId);
208 List<String> result = null;
209 try {
210 result = ZKUtil.listChildrenNoWatch(this.zookeeper, znode);
211 } catch (KeeperException e) {
212 this.abortable.abort("Failed to get list of wals for queueId=" + queueId, e);
213 }
214 return result;
215 }
216
217 @Override
218 public List<String> getAllQueues() {
219 List<String> listOfQueues = null;
220 try {
221 listOfQueues = ZKUtil.listChildrenNoWatch(this.zookeeper, this.myQueuesZnode);
222 } catch (KeeperException e) {
223 this.abortable.abort("Failed to get a list of queues for region server: "
224 + this.myQueuesZnode, e);
225 }
226 return listOfQueues;
227 }
228
229 @Override
230 public boolean isThisOurRegionServer(String regionserver) {
231 return ZKUtil.joinZNode(this.queuesZNode, regionserver).equals(this.myQueuesZnode);
232 }
233
234 @Override
235 public List<String> getUnClaimedQueueIds(String regionserver) {
236 if (isThisOurRegionServer(regionserver)) {
237 return null;
238 }
239 String rsZnodePath = ZKUtil.joinZNode(this.queuesZNode, regionserver);
240 List<String> queues = null;
241 try {
242 queues = ZKUtil.listChildrenNoWatch(this.zookeeper, rsZnodePath);
243 } catch (KeeperException e) {
244 this.abortable.abort("Failed to getUnClaimedQueueIds for " + regionserver, e);
245 }
246 if (queues != null) {
247 queues.remove(RS_LOCK_ZNODE);
248 }
249 return queues;
250 }
251
252 @Override
253 public Pair<String, SortedSet<String>> claimQueue(String regionserver, String queueId) {
254 if (conf.getBoolean(HConstants.ZOOKEEPER_USEMULTI, true)) {
255 LOG.info("Atomically moving " + regionserver + "/" + queueId + "'s WALs to my queue");
256 return moveQueueUsingMulti(regionserver, queueId);
257 } else {
258 LOG.info("Moving " + regionserver + "/" + queueId + "'s wals to my queue");
259 if (!lockOtherRS(regionserver)) {
260 LOG.info("Can not take the lock now");
261 return null;
262 }
263 Pair<String, SortedSet<String>> newQueues;
264 try {
265 newQueues = copyQueueFromLockedRS(regionserver, queueId);
266 removeQueueFromLockedRS(regionserver, queueId);
267 } finally {
268 unlockOtherRS(regionserver);
269 }
270 return newQueues;
271 }
272 }
273
274 private void removeQueueFromLockedRS(String znode, String peerId) {
275 String nodePath = ZKUtil.joinZNode(this.queuesZNode, znode);
276 String peerPath = ZKUtil.joinZNode(nodePath, peerId);
277 try {
278 ZKUtil.deleteNodeRecursively(this.zookeeper, peerPath);
279 } catch (KeeperException e) {
280 LOG.warn("Remove copied queue failed", e);
281 }
282 }
283
284 @Override
285 public void removeReplicatorIfQueueIsEmpty(String regionserver) {
286 String rsPath = ZKUtil.joinZNode(this.queuesZNode, regionserver);
287 try {
288 List<String> list = ZKUtil.listChildrenNoWatch(this.zookeeper, rsPath);
289 if (list != null && list.size() == 0){
290 ZKUtil.deleteNode(this.zookeeper, rsPath);
291 }
292 } catch (KeeperException e) {
293 LOG.warn("Got error while removing replicator", e);
294 }
295 }
296
297
298
299
300
301
302 @VisibleForTesting
303 public boolean lockOtherRS(String znode) {
304 try {
305 String parent = ZKUtil.joinZNode(this.queuesZNode, znode);
306 if (parent.equals(this.myQueuesZnode)) {
307 LOG.warn("Won't lock because this is us, we're dead!");
308 return false;
309 }
310 String p = ZKUtil.joinZNode(parent, RS_LOCK_ZNODE);
311 ZKUtil.createAndWatch(this.zookeeper, p, lockToByteArray(this.myQueuesZnode));
312 } catch (KeeperException e) {
313
314
315
316
317
318 if (e instanceof KeeperException.NoNodeException
319 || e instanceof KeeperException.NodeExistsException) {
320 LOG.info("Won't transfer the queue," + " another RS took care of it because of: "
321 + e.getMessage());
322 } else {
323 LOG.info("Failed lock other rs", e);
324 }
325 return false;
326 }
327 return true;
328 }
329
330 public String getLockZNode(String znode) {
331 return this.queuesZNode + "/" + znode + "/" + RS_LOCK_ZNODE;
332 }
333
334 @VisibleForTesting
335 public boolean checkLockExists(String znode) throws KeeperException {
336 return ZKUtil.checkExists(zookeeper, getLockZNode(znode)) >= 0;
337 }
338
339 private void unlockOtherRS(String znode){
340 String parent = ZKUtil.joinZNode(this.queuesZNode, znode);
341 String p = ZKUtil.joinZNode(parent, RS_LOCK_ZNODE);
342 try {
343 ZKUtil.deleteNode(this.zookeeper, p);
344 } catch (KeeperException e) {
345 this.abortable.abort("Remove lock failed", e);
346 }
347 }
348
349
350
351
352
353 private void deleteAnotherRSQueues(String regionserverZnode) {
354 String fullpath = ZKUtil.joinZNode(this.queuesZNode, regionserverZnode);
355 try {
356 List<String> clusters = ZKUtil.listChildrenNoWatch(this.zookeeper, fullpath);
357 for (String cluster : clusters) {
358
359 if (cluster.equals(RS_LOCK_ZNODE)) {
360 continue;
361 }
362 String fullClusterPath = ZKUtil.joinZNode(fullpath, cluster);
363 ZKUtil.deleteNodeRecursively(this.zookeeper, fullClusterPath);
364 }
365
366 ZKUtil.deleteNodeRecursively(this.zookeeper, fullpath);
367 } catch (KeeperException e) {
368 if (e instanceof KeeperException.NoNodeException
369 || e instanceof KeeperException.NotEmptyException) {
370
371
372
373 if (e.getPath().equals(fullpath)) {
374 return;
375 }
376 }
377 this.abortable.abort("Failed to delete replication queues for region server: "
378 + regionserverZnode, e);
379 }
380 }
381
382
383
384
385
386
387 private Pair<String, SortedSet<String>> moveQueueUsingMulti(String znode, String peerId) {
388 try {
389
390 String deadRSZnodePath = ZKUtil.joinZNode(this.queuesZNode, znode);
391 List<ZKUtilOp> listOfOps = new ArrayList<>();
392 ReplicationQueueInfo replicationQueueInfo = new ReplicationQueueInfo(peerId);
393 if (!peerExists(replicationQueueInfo.getPeerId())) {
394
395
396
397 LOG.warn("Peer " + peerId +
398 " didn't exist, will move its queue to avoid the failure of multi op");
399 }
400 String newPeerId = peerId + "-" + znode;
401 String newPeerZnode = ZKUtil.joinZNode(this.myQueuesZnode, newPeerId);
402
403 String oldClusterZnode = ZKUtil.joinZNode(deadRSZnodePath, peerId);
404 List<String> wals = ZKUtil.listChildrenNoWatch(this.zookeeper, oldClusterZnode);
405 SortedSet<String> logQueue = new TreeSet<>();
406 if (wals == null || wals.size() == 0) {
407 listOfOps.add(ZKUtilOp.deleteNodeFailSilent(oldClusterZnode));
408 } else {
409
410 ZKUtilOp op = ZKUtilOp.createAndFailSilent(newPeerZnode, HConstants.EMPTY_BYTE_ARRAY);
411 listOfOps.add(op);
412
413 for (String wal : wals) {
414 String oldWalZnode = ZKUtil.joinZNode(oldClusterZnode, wal);
415 byte[] logOffset = ZKUtil.getData(this.zookeeper, oldWalZnode);
416 LOG.debug("Creating " + wal + " with data " + Bytes.toString(logOffset));
417 String newLogZnode = ZKUtil.joinZNode(newPeerZnode, wal);
418 listOfOps.add(ZKUtilOp.createAndFailSilent(newLogZnode, logOffset));
419 listOfOps.add(ZKUtilOp.deleteNodeFailSilent(oldWalZnode));
420 logQueue.add(wal);
421 }
422
423 listOfOps.add(ZKUtilOp.deleteNodeFailSilent(oldClusterZnode));
424
425 if (LOG.isTraceEnabled())
426 LOG.trace(" The multi list size is: " + listOfOps.size());
427 }
428 ZKUtil.multiOrSequential(this.zookeeper, listOfOps, false);
429 if (LOG.isTraceEnabled())
430 LOG.trace("Atomically moved the dead regionserver logs. ");
431 return new Pair<>(newPeerId, logQueue);
432 } catch (KeeperException e) {
433
434 LOG.warn("Got exception in copyQueuesFromRSUsingMulti: ", e);
435 } catch (InterruptedException e) {
436 LOG.warn("Got exception in copyQueuesFromRSUsingMulti: ", e);
437 Thread.currentThread().interrupt();
438 }
439 return null;
440 }
441
442
443
444
445
446
447
448 private Pair<String, SortedSet<String>> copyQueueFromLockedRS(String znode, String peerId) {
449
450
451 try {
452 String nodePath = ZKUtil.joinZNode(this.queuesZNode, znode);
453 ReplicationQueueInfo replicationQueueInfo = new ReplicationQueueInfo(peerId);
454 String clusterPath = ZKUtil.joinZNode(nodePath, peerId);
455 if (!peerExists(replicationQueueInfo.getPeerId())) {
456 LOG.warn("Peer " + peerId + " didn't exist, skipping the replay");
457
458 return null;
459 }
460
461
462
463 String newCluster = peerId + "-" + znode;
464 String newClusterZnode = ZKUtil.joinZNode(this.myQueuesZnode, newCluster);
465
466 List<String> wals = ZKUtil.listChildrenNoWatch(this.zookeeper, clusterPath);
467
468 if (wals == null || wals.size() == 0) {
469 return null;
470 }
471 ZKUtil.createNodeIfNotExistsAndWatch(this.zookeeper, newClusterZnode,
472 HConstants.EMPTY_BYTE_ARRAY);
473 SortedSet<String> logQueue = new TreeSet<>();
474 for (String wal : wals) {
475 String z = ZKUtil.joinZNode(clusterPath, wal);
476 byte[] positionBytes = ZKUtil.getData(this.zookeeper, z);
477 long position = 0;
478 try {
479 position = ZKUtil.parseWALPositionFrom(positionBytes);
480 } catch (DeserializationException e) {
481 LOG.warn("Failed parse of wal position from the following znode: " + z
482 + ", Exception: " + e);
483 }
484 LOG.debug("Creating " + wal + " with data " + position);
485 String child = ZKUtil.joinZNode(newClusterZnode, wal);
486
487
488 ZKUtil.createNodeIfNotExistsAndWatch(this.zookeeper, child, positionBytes);
489 logQueue.add(wal);
490 }
491 return new Pair<>(newCluster, logQueue);
492 } catch (KeeperException e) {
493 LOG.warn("Got exception in copyQueueFromLockedRS: "+
494 " Possible problem: check if znode size exceeds jute.maxBuffer value. "
495 + "If so, increase it for both client and server side." ,e);
496
497 } catch (InterruptedException e) {
498 LOG.warn(e);
499 Thread.currentThread().interrupt();
500 }
501 return null;
502 }
503
504
505
506
507
508
509 static byte[] lockToByteArray(final String lockOwner) {
510 byte[] bytes =
511 ZooKeeperProtos.ReplicationLock.newBuilder().setLockOwner(lockOwner).build().toByteArray();
512 return ProtobufUtil.prependPBMagic(bytes);
513 }
514
515 @Override
516 public void addHFileRefs(String peerId, List<String> files) throws ReplicationException {
517 String peerZnode = ZKUtil.joinZNode(this.hfileRefsZNode, peerId);
518 boolean debugEnabled = LOG.isDebugEnabled();
519 if (debugEnabled) {
520 LOG.debug("Adding hfile references " + files + " in queue " + peerZnode);
521 }
522 List<ZKUtilOp> listOfOps = new ArrayList<ZKUtil.ZKUtilOp>();
523 int size = files.size();
524 for (int i = 0; i < size; i++) {
525 listOfOps.add(ZKUtilOp.createAndFailSilent(ZKUtil.joinZNode(peerZnode, files.get(i)),
526 HConstants.EMPTY_BYTE_ARRAY));
527 }
528 if (debugEnabled) {
529 LOG.debug(" The multi list size for adding hfile references in zk for node " + peerZnode
530 + " is " + listOfOps.size());
531 }
532 try {
533 ZKUtil.multiOrSequential(this.zookeeper, listOfOps, true);
534 } catch (KeeperException e) {
535 throw new ReplicationException("Failed to create hfile reference znode=" + e.getPath(), e);
536 }
537 }
538
539 @Override
540 public void removeHFileRefs(String peerId, List<String> files) {
541 String peerZnode = ZKUtil.joinZNode(this.hfileRefsZNode, peerId);
542 boolean debugEnabled = LOG.isDebugEnabled();
543 if (debugEnabled) {
544 LOG.debug("Removing hfile references " + files + " from queue " + peerZnode);
545 }
546 List<ZKUtilOp> listOfOps = new ArrayList<ZKUtil.ZKUtilOp>();
547 int size = files.size();
548 for (int i = 0; i < size; i++) {
549 listOfOps.add(ZKUtilOp.deleteNodeFailSilent(ZKUtil.joinZNode(peerZnode, files.get(i))));
550 }
551 if (debugEnabled) {
552 LOG.debug(" The multi list size for removing hfile references in zk for node " + peerZnode
553 + " is " + listOfOps.size());
554 }
555 try {
556 ZKUtil.multiOrSequential(this.zookeeper, listOfOps, true);
557 } catch (KeeperException e) {
558 LOG.error("Failed to remove hfile reference znode=" + e.getPath(), e);
559 }
560 }
561
562 @Override
563 public void addPeerToHFileRefs(String peerId) throws ReplicationException {
564 String peerZnode = ZKUtil.joinZNode(this.hfileRefsZNode, peerId);
565 try {
566 if (ZKUtil.checkExists(this.zookeeper, peerZnode) == -1) {
567 LOG.info("Adding peer " + peerId + " to hfile reference queue.");
568 ZKUtil.createWithParents(this.zookeeper, peerZnode);
569 }
570 } catch (KeeperException e) {
571 throw new ReplicationException("Failed to add peer " + peerId + " to hfile reference queue.",
572 e);
573 }
574 }
575
576 @Override
577 public void removePeerFromHFileRefs(String peerId) {
578 final String peerZnode = ZKUtil.joinZNode(this.hfileRefsZNode, peerId);
579 try {
580 if (ZKUtil.checkExists(this.zookeeper, peerZnode) == -1) {
581 if (LOG.isDebugEnabled()) {
582 LOG.debug("Peer " + peerZnode + " not found in hfile reference queue.");
583 }
584 return;
585 } else {
586 LOG.info("Removing peer " + peerZnode + " from hfile reference queue.");
587 ZKUtil.deleteNodeRecursively(this.zookeeper, peerZnode);
588 }
589 } catch (KeeperException e) {
590 LOG.error("Ignoring the exception to remove peer " + peerId + " from hfile reference queue.",
591 e);
592 }
593 }
594 }