1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.replication.regionserver;
21
22 import com.google.common.annotations.VisibleForTesting;
23 import com.google.common.util.concurrent.ThreadFactoryBuilder;
24
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Random;
34 import java.util.Set;
35 import java.util.SortedSet;
36 import java.util.TreeSet;
37 import java.util.UUID;
38 import java.util.concurrent.ConcurrentHashMap;
39 import java.util.concurrent.CopyOnWriteArrayList;
40 import java.util.concurrent.LinkedBlockingQueue;
41 import java.util.concurrent.RejectedExecutionException;
42 import java.util.concurrent.ThreadPoolExecutor;
43 import java.util.concurrent.TimeUnit;
44
45 import org.apache.commons.logging.Log;
46 import org.apache.commons.logging.LogFactory;
47 import org.apache.hadoop.conf.Configuration;
48 import org.apache.hadoop.fs.FileSystem;
49 import org.apache.hadoop.fs.Path;
50 import org.apache.hadoop.hbase.HConstants;
51 import org.apache.hadoop.hbase.Server;
52 import org.apache.hadoop.hbase.TableDescriptors;
53 import org.apache.hadoop.hbase.TableName;
54 import org.apache.hadoop.hbase.classification.InterfaceAudience;
55 import org.apache.hadoop.hbase.regionserver.HRegionServer;
56 import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
57 import org.apache.hadoop.hbase.replication.ReplicationEndpoint;
58 import org.apache.hadoop.hbase.replication.ReplicationException;
59 import org.apache.hadoop.hbase.replication.ReplicationListener;
60 import org.apache.hadoop.hbase.replication.ReplicationPeer;
61 import org.apache.hadoop.hbase.replication.ReplicationPeerConfig;
62 import org.apache.hadoop.hbase.replication.ReplicationPeers;
63 import org.apache.hadoop.hbase.replication.ReplicationQueueInfo;
64 import org.apache.hadoop.hbase.replication.ReplicationQueues;
65 import org.apache.hadoop.hbase.replication.ReplicationTracker;
66 import org.apache.hadoop.hbase.util.Pair;
67 import org.apache.hadoop.hbase.wal.DefaultWALProvider;
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 @InterfaceAudience.Private
86 public class ReplicationSourceManager implements ReplicationListener {
87 private static final Log LOG =
88 LogFactory.getLog(ReplicationSourceManager.class);
89
90 private final List<ReplicationSourceInterface> sources;
91
92 private final List<ReplicationSourceInterface> oldsources;
93 private final ReplicationQueues replicationQueues;
94 private final ReplicationTracker replicationTracker;
95 private final ReplicationPeers replicationPeers;
96
97 private final UUID clusterId;
98
99 private final Server server;
100
101
102 private final Map<String, Map<String, SortedSet<String>>> walsById;
103
104 private final Map<String, Map<String, SortedSet<String>>> walsByIdRecoveredQueues;
105 private final Configuration conf;
106 private final FileSystem fs;
107
108 private Set<Path> latestPaths;
109
110 private final Path logDir;
111
112 private final Path oldLogDir;
113
114 private final long sleepBeforeFailover;
115
116 private final ThreadPoolExecutor executor;
117
118 private final Random rand;
119 private final boolean replicationForBulkLoadDataEnabled;
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 public ReplicationSourceManager(final ReplicationQueues replicationQueues,
135 final ReplicationPeers replicationPeers, final ReplicationTracker replicationTracker,
136 final Configuration conf, final Server server, final FileSystem fs, final Path logDir,
137 final Path oldLogDir, final UUID clusterId) {
138
139
140 this.sources = new CopyOnWriteArrayList<ReplicationSourceInterface>();
141 this.replicationQueues = replicationQueues;
142 this.replicationPeers = replicationPeers;
143 this.replicationTracker = replicationTracker;
144 this.server = server;
145 this.walsById = new HashMap<String, Map<String, SortedSet<String>>>();
146 this.walsByIdRecoveredQueues = new ConcurrentHashMap<String, Map<String, SortedSet<String>>>();
147 this.oldsources = new CopyOnWriteArrayList<ReplicationSourceInterface>();
148 this.conf = conf;
149 this.fs = fs;
150 this.logDir = logDir;
151 this.oldLogDir = oldLogDir;
152 this.sleepBeforeFailover =
153 conf.getLong("replication.sleep.before.failover", 30000);
154 this.clusterId = clusterId;
155 this.replicationTracker.registerListener(this);
156 this.replicationPeers.getAllPeerIds();
157
158
159 int nbWorkers = conf.getInt("replication.executor.workers", 1);
160
161
162 this.executor = new ThreadPoolExecutor(nbWorkers, nbWorkers,
163 100, TimeUnit.MILLISECONDS,
164 new LinkedBlockingQueue<Runnable>());
165 ThreadFactoryBuilder tfb = new ThreadFactoryBuilder();
166 tfb.setNameFormat("ReplicationExecutor-%d");
167 tfb.setDaemon(true);
168 this.executor.setThreadFactory(tfb.build());
169 this.rand = new Random();
170 this.latestPaths = Collections.synchronizedSet(new HashSet<Path>());
171 replicationForBulkLoadDataEnabled =
172 conf.getBoolean(HConstants.REPLICATION_BULKLOAD_ENABLE_KEY,
173 HConstants.REPLICATION_BULKLOAD_ENABLE_DEFAULT);
174 }
175
176
177
178
179
180
181
182
183
184
185
186
187 public void logPositionAndCleanOldLogs(Path log, String id, long position,
188 boolean queueRecovered, boolean holdLogInZK) {
189 String fileName = log.getName();
190 this.replicationQueues.setLogPosition(id, fileName, position);
191 if (holdLogInZK) {
192 return;
193 }
194 cleanOldLogs(fileName, id, queueRecovered);
195 }
196
197
198
199
200
201
202
203
204 public void cleanOldLogs(String key, String id, boolean queueRecovered) {
205 String logPrefix = DefaultWALProvider.getWALPrefixFromWALName(key);
206 if (queueRecovered) {
207 SortedSet<String> wals = walsByIdRecoveredQueues.get(id).get(logPrefix);
208 if (wals != null && !wals.first().equals(key)) {
209 cleanOldLogs(wals, key, id);
210 }
211 } else {
212 synchronized (this.walsById) {
213 SortedSet<String> wals = walsById.get(id).get(logPrefix);
214 if (wals != null && !wals.first().equals(key)) {
215 cleanOldLogs(wals, key, id);
216 }
217 }
218 }
219 }
220
221 private void cleanOldLogs(SortedSet<String> wals, String key, String id) {
222 SortedSet<String> walSet = wals.headSet(key);
223 LOG.debug("Removing " + walSet.size() + " logs in the list: " + walSet);
224 for (String wal : walSet) {
225 this.replicationQueues.removeLog(id, wal);
226 }
227 walSet.clear();
228 }
229
230
231
232
233
234 protected void init() throws IOException, ReplicationException {
235 for (String id : this.replicationPeers.getPeerIds()) {
236 addSource(id);
237 if (replicationForBulkLoadDataEnabled) {
238
239
240 this.replicationQueues.addPeerToHFileRefs(id);
241 }
242 }
243 List<String> currentReplicators = this.replicationQueues.getListOfReplicators();
244 if (currentReplicators == null || currentReplicators.size() == 0) {
245 return;
246 }
247 List<String> otherRegionServers = replicationTracker.getListOfRegionServers();
248 LOG.info("Current list of replicators: " + currentReplicators + " other RSs: "
249 + otherRegionServers);
250
251
252 for (String rs : currentReplicators) {
253 if (!otherRegionServers.contains(rs)) {
254 transferQueues(rs);
255 }
256 }
257 }
258
259
260
261
262
263
264
265
266 protected ReplicationSourceInterface addSource(String id) throws IOException,
267 ReplicationException {
268 ReplicationPeerConfig peerConfig = replicationPeers.getReplicationPeerConfig(id);
269 ReplicationPeer peer = replicationPeers.getPeer(id);
270 ReplicationSourceInterface src =
271 getReplicationSource(this.conf, this.fs, this, this.replicationQueues,
272 this.replicationPeers, server, id, this.clusterId, peerConfig, peer);
273 synchronized (this.walsById) {
274 this.sources.add(src);
275 Map<String, SortedSet<String>> walsByGroup = new HashMap<String, SortedSet<String>>();
276 this.walsById.put(id, walsByGroup);
277
278 synchronized (latestPaths) {
279 if (this.latestPaths.size() > 0) {
280 for (Path logPath : latestPaths) {
281 String name = logPath.getName();
282 String walPrefix = DefaultWALProvider.getWALPrefixFromWALName(name);
283 SortedSet<String> logs = new TreeSet<String>();
284 logs.add(name);
285 walsByGroup.put(walPrefix, logs);
286 try {
287 this.replicationQueues.addLog(id, name);
288 } catch (ReplicationException e) {
289 String message =
290 "Cannot add log to queue when creating a new source, queueId=" + id
291 + ", filename=" + name;
292 server.stop(message);
293 throw e;
294 }
295 src.enqueueLog(logPath);
296 }
297 }
298 }
299 }
300 src.startup();
301 return src;
302 }
303
304
305
306
307
308 public void deleteSource(String peerId, boolean closeConnection) {
309 this.replicationQueues.removeQueue(peerId);
310 if (closeConnection) {
311 this.replicationPeers.peerRemoved(peerId);
312 }
313 }
314
315
316
317
318 public void join() {
319 this.executor.shutdown();
320 if (this.sources.size() == 0) {
321 this.replicationQueues.removeAllQueues();
322 }
323 for (ReplicationSourceInterface source : this.sources) {
324 source.terminate("Region server is closing");
325 }
326 }
327
328
329
330
331
332 protected Map<String, Map<String, SortedSet<String>>> getWALs() {
333 return Collections.unmodifiableMap(walsById);
334 }
335
336
337
338
339
340 protected Map<String, Map<String, SortedSet<String>>> getWalsByIdRecoveredQueues() {
341 return Collections.unmodifiableMap(walsByIdRecoveredQueues);
342 }
343
344
345
346
347
348 public List<ReplicationSourceInterface> getSources() {
349 return this.sources;
350 }
351
352
353
354
355
356 public List<ReplicationSourceInterface> getOldSources() {
357 return this.oldsources;
358 }
359
360 @VisibleForTesting
361 List<String> getAllQueues() {
362 return replicationQueues.getAllQueues();
363 }
364
365 void preLogRoll(Path newLog) throws IOException {
366 recordLog(newLog);
367 String logName = newLog.getName();
368 String logPrefix = DefaultWALProvider.getWALPrefixFromWALName(logName);
369 synchronized (latestPaths) {
370 Iterator<Path> iterator = latestPaths.iterator();
371 while (iterator.hasNext()) {
372 Path path = iterator.next();
373 if (path.getName().contains(logPrefix)) {
374 iterator.remove();
375 break;
376 }
377 }
378 this.latestPaths.add(newLog);
379 }
380 }
381
382
383
384
385
386
387
388 private void recordLog(Path logPath) throws IOException {
389 String logName = logPath.getName();
390 String logPrefix = DefaultWALProvider.getWALPrefixFromWALName(logName);
391
392 synchronized (replicationPeers) {
393
394 for (String id : replicationPeers.getPeerIds()) {
395 try {
396 this.replicationQueues.addLog(id, logName);
397 } catch (ReplicationException e) {
398 throw new IOException("Cannot add log to replication queue"
399 + " when creating a new source, queueId=" + id + ", filename=" + logName, e);
400 }
401 }
402 }
403
404 synchronized (walsById) {
405 for (Map.Entry<String, Map<String, SortedSet<String>>> entry : this.walsById.entrySet()) {
406 String peerId = entry.getKey();
407 Map<String, SortedSet<String>> walsByPrefix = entry.getValue();
408 boolean existingPrefix = false;
409 for (Map.Entry<String, SortedSet<String>> walsEntry : walsByPrefix.entrySet()) {
410 SortedSet<String> wals = walsEntry.getValue();
411 if (this.sources.isEmpty()) {
412
413
414 wals.clear();
415 }
416 if (logPrefix.equals(walsEntry.getKey())) {
417 wals.add(logName);
418 existingPrefix = true;
419 }
420 }
421 if (!existingPrefix) {
422
423 LOG.debug("Start tracking logs for wal group " + logPrefix + " for peer " + peerId);
424 SortedSet<String> wals = new TreeSet<String>();
425 wals.add(logName);
426 walsByPrefix.put(logPrefix, wals);
427 }
428 }
429 }
430 }
431
432 void postLogRoll(Path newLog) throws IOException {
433
434 for (ReplicationSourceInterface source : this.sources) {
435 source.enqueueLog(newLog);
436 }
437 }
438
439
440
441
442
443
444
445
446
447
448
449 protected ReplicationSourceInterface getReplicationSource(final Configuration conf,
450 final FileSystem fs, final ReplicationSourceManager manager,
451 final ReplicationQueues replicationQueues, final ReplicationPeers replicationPeers,
452 final Server server, final String peerId, final UUID clusterId,
453 final ReplicationPeerConfig peerConfig, final ReplicationPeer replicationPeer)
454 throws IOException {
455 RegionServerCoprocessorHost rsServerHost = null;
456 TableDescriptors tableDescriptors = null;
457 if (server instanceof HRegionServer) {
458 rsServerHost = ((HRegionServer) server).getRegionServerCoprocessorHost();
459 tableDescriptors = ((HRegionServer) server).getTableDescriptors();
460 }
461 ReplicationSourceInterface src;
462 try {
463 @SuppressWarnings("rawtypes")
464 Class c = Class.forName(conf.get("replication.replicationsource.implementation",
465 ReplicationSource.class.getCanonicalName()));
466 src = (ReplicationSourceInterface) c.newInstance();
467 } catch (Exception e) {
468 LOG.warn("Passed replication source implementation throws errors, " +
469 "defaulting to ReplicationSource", e);
470 src = new ReplicationSource();
471 }
472
473 ReplicationEndpoint replicationEndpoint = null;
474 try {
475 String replicationEndpointImpl = peerConfig.getReplicationEndpointImpl();
476 if (replicationEndpointImpl == null) {
477
478 replicationEndpointImpl = HBaseInterClusterReplicationEndpoint.class.getName();
479 }
480 @SuppressWarnings("rawtypes")
481 Class c = Class.forName(replicationEndpointImpl);
482 replicationEndpoint = (ReplicationEndpoint) c.newInstance();
483 if(rsServerHost != null) {
484 ReplicationEndpoint newReplicationEndPoint = rsServerHost
485 .postCreateReplicationEndPoint(replicationEndpoint);
486 if(newReplicationEndPoint != null) {
487
488 replicationEndpoint = newReplicationEndPoint;
489 }
490 }
491 } catch (Exception e) {
492 LOG.warn("Passed replication endpoint implementation throws errors"
493 + " while initializing ReplicationSource for peer: " + peerId, e);
494 throw new IOException(e);
495 }
496
497 MetricsSource metrics = new MetricsSource(peerId);
498
499 src.init(conf, fs, manager, replicationQueues, replicationPeers, server, peerId,
500 clusterId, replicationEndpoint, metrics);
501
502
503 replicationEndpoint.init(new ReplicationEndpoint.Context(replicationPeer.getConfiguration(),
504 fs, peerConfig, peerId, clusterId, replicationPeer, metrics, tableDescriptors, server));
505 return src;
506 }
507
508
509
510
511
512
513
514
515
516 private void transferQueues(String rsZnode) {
517 NodeFailoverWorker transfer =
518 new NodeFailoverWorker(rsZnode, this.replicationQueues, this.replicationPeers,
519 this.clusterId);
520 try {
521 this.executor.execute(transfer);
522 } catch (RejectedExecutionException ex) {
523 LOG.info("Cancelling the transfer of " + rsZnode + " because of " + ex.getMessage());
524 }
525 }
526
527
528
529
530
531 public void closeRecoveredQueue(ReplicationSourceInterface src) {
532 LOG.info("Done with the recovered queue " + src.getPeerClusterZnode());
533 this.oldsources.remove(src);
534 deleteSource(src.getPeerClusterZnode(), false);
535 this.walsByIdRecoveredQueues.remove(src.getPeerClusterZnode());
536 }
537
538
539
540
541
542 public void closeQueue(ReplicationSourceInterface src) {
543 LOG.info("Done with the queue " + src.getPeerClusterZnode());
544 if (src instanceof ReplicationSource) {
545 ((ReplicationSource) src).getSourceMetrics().clear();
546 }
547 this.sources.remove(src);
548 deleteSource(src.getPeerClusterZnode(), true);
549 this.walsById.remove(src.getPeerClusterZnode());
550 }
551
552
553
554
555
556
557 public void removePeer(String id) {
558 LOG.info("Closing the following queue " + id + ", currently have "
559 + sources.size() + " and another "
560 + oldsources.size() + " that were recovered");
561 String terminateMessage = "Replication stream was removed by a user";
562 List<ReplicationSourceInterface> oldSourcesToDelete =
563 new ArrayList<ReplicationSourceInterface>();
564
565
566 synchronized (oldsources) {
567
568 for (ReplicationSourceInterface src : oldsources) {
569 if (id.equals(src.getPeerClusterId())) {
570 oldSourcesToDelete.add(src);
571 }
572 }
573 for (ReplicationSourceInterface src : oldSourcesToDelete) {
574 src.terminate(terminateMessage);
575 closeRecoveredQueue(src);
576 }
577 }
578 LOG.info("Number of deleted recovered sources for " + id + ": "
579 + oldSourcesToDelete.size());
580
581 List<ReplicationSourceInterface> srcToRemove = new ArrayList<ReplicationSourceInterface>();
582
583 synchronized (this.replicationPeers) {
584 for (ReplicationSourceInterface src : this.sources) {
585 if (id.equals(src.getPeerClusterId())) {
586 srcToRemove.add(src);
587 }
588 }
589 if (srcToRemove.isEmpty()) {
590 LOG.error("The peer we wanted to remove is missing a ReplicationSourceInterface. " +
591 "This could mean that ReplicationSourceInterface initialization failed for this peer " +
592 "and that replication on this peer may not be caught up. peerId=" + id);
593 }
594 for (ReplicationSourceInterface toRemove : srcToRemove) {
595 toRemove.terminate(terminateMessage);
596 this.sources.remove(toRemove);
597 }
598 deleteSource(id, true);
599 }
600 }
601
602 @Override
603 public void regionServerRemoved(String regionserver) {
604 transferQueues(regionserver);
605 }
606
607 @Override
608 public void peerRemoved(String peerId) {
609 removePeer(peerId);
610 this.replicationQueues.removePeerFromHFileRefs(peerId);
611 }
612
613 @Override
614 public void peerListChanged(List<String> peerIds) {
615 for (String id : peerIds) {
616 try {
617 boolean added = this.replicationPeers.peerAdded(id);
618 if (added) {
619 addSource(id);
620 if (replicationForBulkLoadDataEnabled) {
621 this.replicationQueues.addPeerToHFileRefs(id);
622 }
623 }
624 } catch (Exception e) {
625 LOG.error("Error while adding a new peer", e);
626 }
627 }
628 }
629
630
631
632
633
634 class NodeFailoverWorker extends Thread {
635
636 private String rsZnode;
637 private final ReplicationQueues rq;
638 private final ReplicationPeers rp;
639 private final UUID clusterId;
640
641
642
643
644 public NodeFailoverWorker(String rsZnode) {
645 this(rsZnode, replicationQueues, replicationPeers, ReplicationSourceManager.this.clusterId);
646 }
647
648 public NodeFailoverWorker(String rsZnode, final ReplicationQueues replicationQueues,
649 final ReplicationPeers replicationPeers, final UUID clusterId) {
650 super("Failover-for-"+rsZnode);
651 this.rsZnode = rsZnode;
652 this.rq = replicationQueues;
653 this.rp = replicationPeers;
654 this.clusterId = clusterId;
655 }
656
657 @Override
658 public void run() {
659 if (this.rq.isThisOurZnode(rsZnode)) {
660 return;
661 }
662
663
664 try {
665 Thread.sleep(sleepBeforeFailover + (long) (rand.nextFloat() * sleepBeforeFailover));
666 } catch (InterruptedException e) {
667 LOG.warn("Interrupted while waiting before transferring a queue.");
668 Thread.currentThread().interrupt();
669 }
670
671 if (server.isStopped()) {
672 LOG.info("Not transferring queue since we are shutting down");
673 return;
674 }
675
676 Map<String, SortedSet<String>> newQueues = new HashMap<>();
677 List<String> peers = rq.getUnClaimedQueueIds(rsZnode);
678 while (peers != null && !peers.isEmpty()) {
679 Pair<String, SortedSet<String>> peer = this.rq.claimQueue(rsZnode,
680 peers.get(rand.nextInt(peers.size())));
681 long sleep = sleepBeforeFailover/2;
682 if (peer != null) {
683 newQueues.put(peer.getFirst(), peer.getSecond());
684 sleep = sleepBeforeFailover;
685 }
686 try {
687 Thread.sleep(sleep);
688 } catch (InterruptedException e) {
689 LOG.warn("Interrupted while waiting before transferring a queue.");
690 Thread.currentThread().interrupt();
691 }
692 peers = rq.getUnClaimedQueueIds(rsZnode);
693 }
694 if (peers != null) {
695 rq.removeReplicatorIfQueueIsEmpty(rsZnode);
696 }
697
698
699 if (newQueues.isEmpty()) {
700
701
702 return;
703 }
704
705 for (Map.Entry<String, SortedSet<String>> entry : newQueues.entrySet()) {
706 String peerId = entry.getKey();
707 SortedSet<String> walsSet = entry.getValue();
708 try {
709
710 ReplicationQueueInfo replicationQueueInfo = new ReplicationQueueInfo(peerId);
711 String actualPeerId = replicationQueueInfo.getPeerId();
712 ReplicationPeer peer = replicationPeers.getPeer(actualPeerId);
713 ReplicationPeerConfig peerConfig = null;
714 try {
715 peerConfig = replicationPeers.getReplicationPeerConfig(actualPeerId);
716 } catch (ReplicationException ex) {
717 LOG.warn("Received exception while getting replication peer config, skipping replay"
718 + ex);
719 }
720 if (peer == null || peerConfig == null) {
721 LOG.warn("Skipping failover for peer:" + actualPeerId + " of node" + rsZnode);
722 replicationQueues.removeQueue(peerId);
723 continue;
724 }
725
726 Map<String, SortedSet<String>> walsByGroup = new HashMap<String, SortedSet<String>>();
727 walsByIdRecoveredQueues.put(peerId, walsByGroup);
728 for (String wal : walsSet) {
729 String walPrefix = DefaultWALProvider.getWALPrefixFromWALName(wal);
730 SortedSet<String> wals = walsByGroup.get(walPrefix);
731 if (wals == null) {
732 wals = new TreeSet<String>();
733 walsByGroup.put(walPrefix, wals);
734 }
735 wals.add(wal);
736 }
737
738
739 ReplicationSourceInterface src =
740 getReplicationSource(conf, fs, ReplicationSourceManager.this, this.rq, this.rp,
741 server, peerId, this.clusterId, peerConfig, peer);
742
743
744 synchronized (oldsources) {
745 if (!this.rp.getPeerIds().contains(src.getPeerClusterId())) {
746 src.terminate("Recovered queue doesn't belong to any current peer");
747 closeRecoveredQueue(src);
748 continue;
749 }
750 oldsources.add(src);
751 for (String wal : walsSet) {
752 src.enqueueLog(new Path(oldLogDir, wal));
753 }
754 src.startup();
755 }
756 } catch (IOException e) {
757
758 LOG.error("Failed creating a source", e);
759 }
760 }
761 }
762 }
763
764
765
766
767
768 public Path getOldLogDir() {
769 return this.oldLogDir;
770 }
771
772
773
774
775
776 public Path getLogDir() {
777 return this.logDir;
778 }
779
780
781
782
783
784 public FileSystem getFs() {
785 return this.fs;
786 }
787
788
789
790
791
792 public ReplicationPeers getReplicationPeers() {return this.replicationPeers;}
793
794
795
796
797 public String getStats() {
798 StringBuffer stats = new StringBuffer();
799 for (ReplicationSourceInterface source : sources) {
800 stats.append("Normal source for cluster " + source.getPeerClusterId() + ": ");
801 stats.append(source.getStats() + "\n");
802 }
803 for (ReplicationSourceInterface oldSource : oldsources) {
804 stats.append("Recovered source for cluster/machine(s) " + oldSource.getPeerClusterId()+": ");
805 stats.append(oldSource.getStats()+ "\n");
806 }
807 return stats.toString();
808 }
809
810 public void addHFileRefs(TableName tableName, byte[] family, List<String> files)
811 throws ReplicationException {
812 for (ReplicationSourceInterface source : this.sources) {
813 source.addHFileRefs(tableName, family, files);
814 }
815 }
816
817 public void cleanUpHFileRefs(String peerId, List<String> files) {
818 this.replicationQueues.removeHFileRefs(peerId, files);
819 }
820 }