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