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.io.IOException;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.TreeMap;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.hadoop.hbase.HBaseConfiguration;
33 import org.apache.hadoop.hbase.classification.InterfaceAudience;
34 import org.apache.hadoop.conf.Configuration;
35 import org.apache.hadoop.hbase.Abortable;
36 import org.apache.hadoop.hbase.CompoundConfiguration;
37 import org.apache.hadoop.hbase.TableName;
38 import org.apache.hadoop.hbase.exceptions.DeserializationException;
39 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
40 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.BytesBytesPair;
41 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameStringPair;
42 import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
43 import org.apache.hadoop.hbase.replication.ReplicationPeer.PeerState;
44 import org.apache.hadoop.hbase.util.Bytes;
45 import org.apache.hadoop.hbase.util.Pair;
46 import org.apache.hadoop.hbase.zookeeper.ZKUtil;
47 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
48 import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp;
49 import org.apache.zookeeper.KeeperException;
50
51 import com.google.protobuf.ByteString;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 @InterfaceAudience.Private
80 public class ReplicationPeersZKImpl extends ReplicationStateZKBase implements ReplicationPeers {
81
82
83 private Map<String, ReplicationPeerZKImpl> peerClusters;
84 private final String tableCFsNodeName;
85 private final ReplicationQueuesClient queuesClient;
86
87 private static final Log LOG = LogFactory.getLog(ReplicationPeersZKImpl.class);
88
89 public ReplicationPeersZKImpl(final ZooKeeperWatcher zk, final Configuration conf,
90 final ReplicationQueuesClient queuesClient, Abortable abortable) {
91 super(zk, conf, abortable);
92 this.tableCFsNodeName = conf.get("zookeeper.znode.replication.peers.tableCFs", "tableCFs");
93 this.peerClusters = new ConcurrentHashMap<String, ReplicationPeerZKImpl>();
94 this.queuesClient = queuesClient;
95 }
96
97 @Override
98 public void init() throws ReplicationException {
99 try {
100 if (ZKUtil.checkExists(this.zookeeper, this.peersZNode) < 0) {
101 ZKUtil.createWithParents(this.zookeeper, this.peersZNode);
102 }
103 } catch (KeeperException e) {
104 throw new ReplicationException("Could not initialize replication peers", e);
105 }
106 addExistingPeers();
107 }
108
109 @Override
110 public void addPeer(String id, ReplicationPeerConfig peerConfig, String tableCFs)
111 throws ReplicationException {
112 try {
113 if (peerExists(id)) {
114 throw new IllegalArgumentException("Cannot add a peer with id=" + id
115 + " because that id already exists.");
116 }
117
118 if(id.contains("-")){
119 throw new IllegalArgumentException("Found invalid peer name:" + id);
120 }
121
122 checkQueuesDeleted(id);
123
124 ZKUtil.createWithParents(this.zookeeper, this.peersZNode);
125 List<ZKUtilOp> listOfOps = new ArrayList<ZKUtil.ZKUtilOp>();
126 ZKUtilOp op1 = ZKUtilOp.createAndFailSilent(ZKUtil.joinZNode(this.peersZNode, id),
127 toByteArray(peerConfig));
128
129
130
131
132 ZKUtilOp op2 = ZKUtilOp.createAndFailSilent(getPeerStateNode(id), ENABLED_ZNODE_BYTES);
133 String tableCFsStr = (tableCFs == null) ? "" : tableCFs;
134 ZKUtilOp op3 = ZKUtilOp.createAndFailSilent(getTableCFsNode(id), Bytes.toBytes(tableCFsStr));
135 listOfOps.add(op1);
136 listOfOps.add(op2);
137 listOfOps.add(op3);
138 ZKUtil.multiOrSequential(this.zookeeper, listOfOps, false);
139
140 } catch (KeeperException e) {
141 throw new ReplicationException("Could not add peer with id=" + id
142 + ", peerConfif=>" + peerConfig, e);
143 }
144 }
145
146 @Override
147 public void removePeer(String id) throws ReplicationException {
148 try {
149 if (!peerExists(id)) {
150 throw new IllegalArgumentException("Cannot remove peer with id=" + id
151 + " because that id does not exist.");
152 }
153 ZKUtil.deleteNodeRecursively(this.zookeeper, ZKUtil.joinZNode(this.peersZNode, id));
154 } catch (KeeperException e) {
155 throw new ReplicationException("Could not remove peer with id=" + id, e);
156 }
157 }
158
159 @Override
160 public void enablePeer(String id) throws ReplicationException {
161 changePeerState(id, ZooKeeperProtos.ReplicationState.State.ENABLED);
162 LOG.info("peer " + id + " is enabled");
163 }
164
165 @Override
166 public void disablePeer(String id) throws ReplicationException {
167 changePeerState(id, ZooKeeperProtos.ReplicationState.State.DISABLED);
168 LOG.info("peer " + id + " is disabled");
169 }
170
171 @Override
172 public String getPeerTableCFsConfig(String id) throws ReplicationException {
173 try {
174 if (!peerExists(id)) {
175 throw new IllegalArgumentException("peer " + id + " doesn't exist");
176 }
177 try {
178 return Bytes.toString(ZKUtil.getData(this.zookeeper, getTableCFsNode(id)));
179 } catch (Exception e) {
180 throw new ReplicationException(e);
181 }
182 } catch (KeeperException e) {
183 throw new ReplicationException("Unable to get tableCFs of the peer with id=" + id, e);
184 }
185 }
186
187 @Override
188 public void setPeerTableCFsConfig(String id, String tableCFsStr) throws ReplicationException {
189 try {
190 if (!peerExists(id)) {
191 throw new IllegalArgumentException("Cannot set peer tableCFs because id=" + id
192 + " does not exist.");
193 }
194 String tableCFsZKNode = getTableCFsNode(id);
195 byte[] tableCFs = Bytes.toBytes(tableCFsStr);
196 if (ZKUtil.checkExists(this.zookeeper, tableCFsZKNode) != -1) {
197 ZKUtil.setData(this.zookeeper, tableCFsZKNode, tableCFs);
198 } else {
199 ZKUtil.createAndWatch(this.zookeeper, tableCFsZKNode, tableCFs);
200 }
201 LOG.info("Peer tableCFs with id= " + id + " is now " + tableCFsStr);
202 } catch (KeeperException e) {
203 throw new ReplicationException("Unable to change tableCFs of the peer with id=" + id, e);
204 }
205 }
206
207 @Override
208 public Map<TableName, List<String>> getTableCFs(String id) throws IllegalArgumentException {
209 ReplicationPeer replicationPeer = this.peerClusters.get(id);
210 if (replicationPeer == null) {
211 throw new IllegalArgumentException("Peer with id= " + id + " is not connected");
212 }
213 return replicationPeer.getTableCFs();
214 }
215
216 @Override
217 public boolean getStatusOfPeer(String id) {
218 ReplicationPeer replicationPeer = this.peerClusters.get(id);
219 if (replicationPeer == null) {
220 throw new IllegalArgumentException("Peer with id= " + id + " is not connected");
221 }
222 return replicationPeer.getPeerState() == PeerState.ENABLED;
223 }
224
225 @Override
226 public boolean getStatusOfPeerFromBackingStore(String id) throws ReplicationException {
227 try {
228 if (!peerExists(id)) {
229 throw new IllegalArgumentException("peer " + id + " doesn't exist");
230 }
231 String peerStateZNode = getPeerStateNode(id);
232 try {
233 return ReplicationPeerZKImpl.isStateEnabled(ZKUtil.getData(this.zookeeper, peerStateZNode));
234 } catch (KeeperException e) {
235 throw new ReplicationException(e);
236 } catch (DeserializationException e) {
237 throw new ReplicationException(e);
238 }
239 } catch (KeeperException e) {
240 throw new ReplicationException("Unable to get status of the peer with id=" + id +
241 " from backing store", e);
242 } catch (InterruptedException e) {
243 throw new ReplicationException(e);
244 }
245 }
246
247 @Override
248 public Map<String, ReplicationPeerConfig> getAllPeerConfigs() {
249 Map<String, ReplicationPeerConfig> peers = new TreeMap<String, ReplicationPeerConfig>();
250 List<String> ids = null;
251 try {
252 ids = ZKUtil.listChildrenNoWatch(this.zookeeper, this.peersZNode);
253 for (String id : ids) {
254 ReplicationPeerConfig peerConfig = getReplicationPeerConfig(id);
255 if (peerConfig == null) {
256 LOG.warn("Failed to get replication peer configuration of clusterid=" + id
257 + " znode content, continuing.");
258 continue;
259 }
260 peers.put(id, peerConfig);
261 }
262 } catch (KeeperException e) {
263 this.abortable.abort("Cannot get the list of peers ", e);
264 } catch (ReplicationException e) {
265 this.abortable.abort("Cannot get the list of peers ", e);
266 }
267 return peers;
268 }
269
270 @Override
271 public ReplicationPeer getPeer(String peerId) {
272 return peerClusters.get(peerId);
273 }
274
275 @Override
276 public Set<String> getPeerIds() {
277 return peerClusters.keySet();
278 }
279
280
281
282
283 @Override
284 public ReplicationPeerConfig getReplicationPeerConfig(String peerId)
285 throws ReplicationException {
286 String znode = ZKUtil.joinZNode(this.peersZNode, peerId);
287 byte[] data = null;
288 try {
289 data = ZKUtil.getData(this.zookeeper, znode);
290 } catch (InterruptedException e) {
291 LOG.warn("Could not get configuration for peer because the thread " +
292 "was interrupted. peerId=" + peerId);
293 Thread.currentThread().interrupt();
294 return null;
295 } catch (KeeperException e) {
296 throw new ReplicationException("Error getting configuration for peer with id="
297 + peerId, e);
298 }
299 if (data == null) {
300 LOG.error("Could not get configuration for peer because it doesn't exist. peerId=" + peerId);
301 return null;
302 }
303
304 try {
305 return parsePeerFrom(data);
306 } catch (DeserializationException e) {
307 LOG.warn("Failed to parse cluster key from peerId=" + peerId
308 + ", specifically the content from the following znode: " + znode);
309 return null;
310 }
311 }
312
313 @Override
314 public Pair<ReplicationPeerConfig, Configuration> getPeerConf(String peerId)
315 throws ReplicationException {
316 ReplicationPeerConfig peerConfig = getReplicationPeerConfig(peerId);
317
318 if (peerConfig == null) {
319 return null;
320 }
321
322 Configuration otherConf;
323 try {
324 otherConf = HBaseConfiguration.createClusterConf(this.conf, peerConfig.getClusterKey());
325 } catch (IOException e) {
326 LOG.error("Can't get peer configuration for peerId=" + peerId + " because:", e);
327 return null;
328 }
329
330 if (!peerConfig.getConfiguration().isEmpty()) {
331 CompoundConfiguration compound = new CompoundConfiguration();
332 compound.add(otherConf);
333 compound.addStringMap(peerConfig.getConfiguration());
334 return new Pair<ReplicationPeerConfig, Configuration>(peerConfig, compound);
335 }
336
337 return new Pair<ReplicationPeerConfig, Configuration>(peerConfig, otherConf);
338 }
339
340
341
342
343 @Override
344 public List<String> getAllPeerIds() {
345 List<String> ids = null;
346 try {
347 ids = ZKUtil.listChildrenAndWatchThem(this.zookeeper, this.peersZNode);
348 } catch (KeeperException e) {
349 this.abortable.abort("Cannot get the list of peers ", e);
350 }
351 return ids;
352 }
353
354
355
356
357
358 private void addExistingPeers() throws ReplicationException {
359 List<String> znodes = null;
360 try {
361 znodes = ZKUtil.listChildrenNoWatch(this.zookeeper, this.peersZNode);
362 } catch (KeeperException e) {
363 throw new ReplicationException("Error getting the list of peer clusters.", e);
364 }
365 if (znodes != null) {
366 for (String z : znodes) {
367 createAndAddPeer(z);
368 }
369 }
370 }
371
372 @Override
373 public boolean peerAdded(String peerId) throws ReplicationException {
374 return createAndAddPeer(peerId);
375 }
376
377 @Override
378 public void peerRemoved(String peerId) {
379 ReplicationPeer rp = this.peerClusters.get(peerId);
380 if (rp != null) {
381 ((ConcurrentMap<String, ReplicationPeerZKImpl>) peerClusters).remove(peerId, rp);
382 }
383 }
384
385
386
387
388
389
390 public boolean createAndAddPeer(String peerId) throws ReplicationException {
391 if (peerClusters == null) {
392 return false;
393 }
394 if (this.peerClusters.containsKey(peerId)) {
395 return false;
396 }
397
398 ReplicationPeerZKImpl peer = null;
399 try {
400 peer = createPeer(peerId);
401 } catch (Exception e) {
402 throw new ReplicationException("Error adding peer with id=" + peerId, e);
403 }
404 if (peer == null) {
405 return false;
406 }
407 ReplicationPeerZKImpl previous =
408 ((ConcurrentMap<String, ReplicationPeerZKImpl>) peerClusters).putIfAbsent(peerId, peer);
409 if (previous == null) {
410 LOG.info("Added new peer cluster=" + peer.getPeerConfig().getClusterKey());
411 } else {
412 LOG.info("Peer already present, " + previous.getPeerConfig().getClusterKey() +
413 ", new cluster=" + peer.getPeerConfig().getClusterKey());
414 }
415 return true;
416 }
417
418 private String getTableCFsNode(String id) {
419 return ZKUtil.joinZNode(this.peersZNode, ZKUtil.joinZNode(id, this.tableCFsNodeName));
420 }
421
422 private String getPeerStateNode(String id) {
423 return ZKUtil.joinZNode(this.peersZNode, ZKUtil.joinZNode(id, this.peerStateNodeName));
424 }
425
426
427
428
429
430
431 private void changePeerState(String id, ZooKeeperProtos.ReplicationState.State state)
432 throws ReplicationException {
433 try {
434 if (!peerExists(id)) {
435 throw new IllegalArgumentException("Cannot enable/disable peer because id=" + id
436 + " does not exist.");
437 }
438 String peerStateZNode = getPeerStateNode(id);
439 byte[] stateBytes =
440 (state == ZooKeeperProtos.ReplicationState.State.ENABLED) ? ENABLED_ZNODE_BYTES
441 : DISABLED_ZNODE_BYTES;
442 if (ZKUtil.checkExists(this.zookeeper, peerStateZNode) != -1) {
443 ZKUtil.setData(this.zookeeper, peerStateZNode, stateBytes);
444 } else {
445 ZKUtil.createAndWatch(this.zookeeper, peerStateZNode, stateBytes);
446 }
447 LOG.info("Peer with id= " + id + " is now " + state.name());
448 } catch (KeeperException e) {
449 throw new ReplicationException("Unable to change state of the peer with id=" + id, e);
450 }
451 }
452
453
454
455
456
457
458
459 private ReplicationPeerZKImpl createPeer(String peerId) throws ReplicationException {
460 Pair<ReplicationPeerConfig, Configuration> pair = getPeerConf(peerId);
461 if (pair == null) {
462 return null;
463 }
464 Configuration peerConf = pair.getSecond();
465
466 ReplicationPeerZKImpl peer = new ReplicationPeerZKImpl(peerConf, peerId, pair.getFirst());
467 try {
468 peer.startStateTracker(this.zookeeper, this.getPeerStateNode(peerId));
469 } catch (KeeperException e) {
470 throw new ReplicationException("Error starting the peer state tracker for peerId=" +
471 peerId, e);
472 }
473
474 try {
475 peer.startTableCFsTracker(this.zookeeper, this.getTableCFsNode(peerId));
476 } catch (KeeperException e) {
477 throw new ReplicationException("Error starting the peer tableCFs tracker for peerId=" +
478 peerId, e);
479 }
480
481 return peer;
482 }
483
484
485
486
487
488
489 private static ReplicationPeerConfig parsePeerFrom(final byte[] bytes)
490 throws DeserializationException {
491 if (ProtobufUtil.isPBMagicPrefix(bytes)) {
492 int pblen = ProtobufUtil.lengthOfPBMagic();
493 ZooKeeperProtos.ReplicationPeer.Builder builder =
494 ZooKeeperProtos.ReplicationPeer.newBuilder();
495 ZooKeeperProtos.ReplicationPeer peer;
496 try {
497 ProtobufUtil.mergeFrom(builder, bytes, pblen, bytes.length - pblen);
498 peer = builder.build();
499 } catch (IOException e) {
500 throw new DeserializationException(e);
501 }
502 return convert(peer);
503 } else {
504 if (bytes.length > 0) {
505 return new ReplicationPeerConfig().setClusterKey(Bytes.toString(bytes));
506 }
507 return new ReplicationPeerConfig().setClusterKey("");
508 }
509 }
510
511 private static ReplicationPeerConfig convert(ZooKeeperProtos.ReplicationPeer peer) {
512 ReplicationPeerConfig peerConfig = new ReplicationPeerConfig();
513 if (peer.hasClusterkey()) {
514 peerConfig.setClusterKey(peer.getClusterkey());
515 }
516 if (peer.hasReplicationEndpointImpl()) {
517 peerConfig.setReplicationEndpointImpl(peer.getReplicationEndpointImpl());
518 }
519
520 for (BytesBytesPair pair : peer.getDataList()) {
521 peerConfig.getPeerData().put(pair.getFirst().toByteArray(), pair.getSecond().toByteArray());
522 }
523
524 for (NameStringPair pair : peer.getConfigurationList()) {
525 peerConfig.getConfiguration().put(pair.getName(), pair.getValue());
526 }
527 return peerConfig;
528 }
529
530 private static ZooKeeperProtos.ReplicationPeer convert(ReplicationPeerConfig peerConfig) {
531 ZooKeeperProtos.ReplicationPeer.Builder builder = ZooKeeperProtos.ReplicationPeer.newBuilder();
532 if (peerConfig.getClusterKey() != null) {
533 builder.setClusterkey(peerConfig.getClusterKey());
534 }
535 if (peerConfig.getReplicationEndpointImpl() != null) {
536 builder.setReplicationEndpointImpl(peerConfig.getReplicationEndpointImpl());
537 }
538
539 for (Map.Entry<byte[], byte[]> entry : peerConfig.getPeerData().entrySet()) {
540 builder.addData(BytesBytesPair.newBuilder()
541 .setFirst(ByteString.copyFrom(entry.getKey()))
542 .setSecond(ByteString.copyFrom(entry.getValue()))
543 .build());
544 }
545
546 for (Map.Entry<String, String> entry : peerConfig.getConfiguration().entrySet()) {
547 builder.addConfiguration(NameStringPair.newBuilder()
548 .setName(entry.getKey())
549 .setValue(entry.getValue())
550 .build());
551 }
552
553 return builder.build();
554 }
555
556
557
558
559
560
561
562 private static byte[] toByteArray(final ReplicationPeerConfig peerConfig) {
563 byte[] bytes = convert(peerConfig).toByteArray();
564 return ProtobufUtil.prependPBMagic(bytes);
565 }
566
567 private void checkQueuesDeleted(String peerId) throws ReplicationException {
568 if (queuesClient == null) return;
569 try {
570 List<String> replicators = queuesClient.getListOfReplicators();
571 for (String replicator : replicators) {
572 List<String> queueIds = queuesClient.getAllQueues(replicator);
573 for (String queueId : queueIds) {
574 ReplicationQueueInfo queueInfo = new ReplicationQueueInfo(queueId);
575 if (queueInfo.getPeerId().equals(peerId)) {
576 throw new ReplicationException("undeleted queue for peerId: " + peerId
577 + ", replicator: " + replicator + ", queueId: " + queueId);
578 }
579 }
580 }
581 } catch (KeeperException e) {
582 throw new ReplicationException("Could not check queues deleted with id=" + peerId, e);
583 }
584 }
585 }