View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
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   * This class provides an implementation of the ReplicationQueues interface using Zookeeper. The
48   * base znode that this class works at is the myQueuesZnode. The myQueuesZnode contains a list of
49   * all outstanding WAL files on this region server that need to be replicated. The myQueuesZnode is
50   * the regionserver name (a concatenation of the region server’s hostname, client port and start
51   * code). For example:
52   *
53   * /hbase/replication/rs/hostname.example.org,6020,1234
54   *
55   * Within this znode, the region server maintains a set of WAL replication queues. These queues are
56   * represented by child znodes named using there give queue id. For example:
57   *
58   * /hbase/replication/rs/hostname.example.org,6020,1234/1
59   * /hbase/replication/rs/hostname.example.org,6020,1234/2
60   *
61   * Each queue has one child znode for every WAL that still needs to be replicated. The value of
62   * these WAL child znodes is the latest position that has been replicated. This position is updated
63   * every time a WAL entry is replicated. For example:
64   *
65   * /hbase/replication/rs/hostname.example.org,6020,1234/1/23522342.23422 [VALUE: 254]
66   */
67  @InterfaceAudience.Private
68  public class ReplicationQueuesZKImpl extends ReplicationStateZKBase implements ReplicationQueues {
69  
70    /** Znode containing all replication queues for this region server. */
71    private String myQueuesZnode;
72    /** Name of znode we use to lock during failover */
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       // Why serialize String of Long and not Long as bytes?
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     // if we can not parse the position, start at the beginning of the wal file
183     // again
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       // if the znode is already expired, don't bother going further
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    * Try to set a lock in another region server's znode.
300    * @param znode the server names of the other server
301    * @return true if the lock was acquired, false in every other cases
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       // This exception will pop up if the znode under which we're trying to
315       // create the lock is already deleted by another region server, meaning
316       // that the transfer already occurred.
317       // NoNode => transfer is done and znodes are already deleted
318       // NodeExists => lock znode already created by another RS
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    * Delete all the replication queues for a given region server.
352    * @param regionserverZnode The znode of the region server to delete.
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         // No need to delete, it will be deleted later.
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       // Finish cleaning up
367       ZKUtil.deleteNodeRecursively(this.zookeeper, fullpath);
368     } catch (KeeperException e) {
369       if (e instanceof KeeperException.NoNodeException
370           || e instanceof KeeperException.NotEmptyException) {
371         // Testing a special case where another region server was able to
372         // create a lock just after we deleted it, but then was also able to
373         // delete the RS znode before us or its lock znode is still there.
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    * It "atomically" copies one peer's wals queue from another dead region server and returns them
385    * all sorted. The new peer id is equal to the old peer id appended with the dead server's znode.
386    * @param znode pertaining to the region server to copy the queues from
387    * @peerId peerId pertaining to the queue need to be copied
388    */
389   private Pair<String, SortedSet<String>> moveQueueUsingMulti(String znode, String peerId) {
390     try {
391       // hbase/replication/rs/deadrs
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       // check the logs queue for the old peer cluster
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         // create the new cluster znode
419         ZKUtilOp op = ZKUtilOp.createAndFailSilent(newPeerZnode, HConstants.EMPTY_BYTE_ARRAY);
420         listOfOps.add(op);
421         // get the offset of the logs and set it to new znodes
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         // add delete op for peer
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       // Multi call failed; it looks like some other regionserver took away the logs.
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    * This methods moves all the wals queues from another region server and returns them all sorted
453    * per peer cluster (appended with the dead server's znode)
454    * @param znode server names to copy
455    * @return all wals for the peer of that cluster, null if an error occurred
456    */
457   private Pair<String, SortedSet<String>> copyQueueFromLockedRS(String znode, String peerId) {
458     // TODO this method isn't atomic enough, we could start copying and then
459     // TODO fail for some reason and we would end up with znodes we don't want.
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         // Protection against moving orphaned queues
467         return null;
468       }
469       // We add the name of the recovered RS to the new znode, we can even
470       // do that for queues that were recovered 10 times giving a znode like
471       // number-startcode-number-otherstartcode-number-anotherstartcode-etc
472       String newCluster = peerId + "-" + znode;
473       String newClusterZnode = ZKUtil.joinZNode(this.myQueuesZnode, newCluster);
474 
475       List<String> wals = ZKUtil.listChildrenNoWatch(this.zookeeper, clusterPath);
476       // That region server didn't have anything to replicate for this cluster
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         // Position doesn't actually change, we are just deserializing it for
496         // logging, so just use the already serialized version
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    * @param lockOwner
515    * @return Serialized protobuf of <code>lockOwner</code> with pb magic prefix prepended suitable
516    *         for use as content of an replication lock during region server fail over.
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 }