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