1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.client.replication;
20
21 import com.google.common.annotations.VisibleForTesting;
22
23 import java.io.Closeable;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31
32 import org.apache.commons.lang.StringUtils;
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.hadoop.conf.Configuration;
36 import org.apache.hadoop.hbase.Abortable;
37 import org.apache.hadoop.hbase.HColumnDescriptor;
38 import org.apache.hadoop.hbase.HConstants;
39 import org.apache.hadoop.hbase.HTableDescriptor;
40 import org.apache.hadoop.hbase.TableName;
41 import org.apache.hadoop.hbase.TableNotFoundException;
42 import org.apache.hadoop.hbase.classification.InterfaceAudience;
43 import org.apache.hadoop.hbase.classification.InterfaceStability;
44 import org.apache.hadoop.hbase.client.HBaseAdmin;
45 import org.apache.hadoop.hbase.client.HConnection;
46 import org.apache.hadoop.hbase.client.HConnectionManager;
47 import org.apache.hadoop.hbase.client.HTable;
48 import org.apache.hadoop.hbase.replication.ReplicationException;
49 import org.apache.hadoop.hbase.replication.ReplicationFactory;
50 import org.apache.hadoop.hbase.replication.ReplicationPeer;
51 import org.apache.hadoop.hbase.replication.ReplicationPeerConfig;
52 import org.apache.hadoop.hbase.replication.ReplicationPeerZKImpl;
53 import org.apache.hadoop.hbase.replication.ReplicationPeers;
54 import org.apache.hadoop.hbase.replication.ReplicationQueuesClient;
55 import org.apache.hadoop.hbase.util.Pair;
56 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 @InterfaceAudience.Public
82 @InterfaceStability.Evolving
83 public class ReplicationAdmin implements Closeable {
84 private static final Log LOG = LogFactory.getLog(ReplicationAdmin.class);
85
86 public static final String TNAME = "tableName";
87 public static final String CFNAME = "columnFamlyName";
88
89
90
91 public static final String REPLICATIONTYPE = "replicationType";
92 public static final String REPLICATIONGLOBAL = Integer
93 .toString(HConstants.REPLICATION_SCOPE_GLOBAL);
94
95 private final HConnection connection;
96
97
98 private final ReplicationQueuesClient replicationQueuesClient;
99 private final ReplicationPeers replicationPeers;
100
101
102
103
104 private final ZooKeeperWatcher zkw;
105
106
107
108
109
110
111
112 public ReplicationAdmin(Configuration conf) throws IOException {
113 if (!conf.getBoolean(HConstants.REPLICATION_ENABLE_KEY,
114 HConstants.REPLICATION_ENABLE_DEFAULT)) {
115 throw new RuntimeException("hbase.replication isn't true, please " +
116 "enable it in order to use replication");
117 }
118 this.connection = HConnectionManager.getConnection(conf);
119 try {
120 zkw = createZooKeeperWatcher();
121 try {
122 this.replicationPeers = ReplicationFactory.getReplicationPeers(zkw, conf, this.connection);
123 this.replicationPeers.init();
124 this.replicationQueuesClient =
125 ReplicationFactory.getReplicationQueuesClient(zkw, conf, this.connection);
126 this.replicationQueuesClient.init();
127 } catch (Exception exception) {
128 if (zkw != null) {
129 zkw.close();
130 }
131 throw exception;
132 }
133 } catch (Exception exception) {
134 if (connection != null) {
135 connection.close();
136 }
137 if (exception instanceof IOException) {
138 throw (IOException) exception;
139 } else if (exception instanceof RuntimeException) {
140 throw (RuntimeException) exception;
141 } else {
142 throw new IOException("Error initializing the replication admin client.", exception);
143 }
144 }
145 }
146
147 private ZooKeeperWatcher createZooKeeperWatcher() throws IOException {
148
149 return new ZooKeeperWatcher(connection.getConfiguration(), "ReplicationAdmin", new Abortable() {
150 @Override
151 public void abort(String why, Throwable e) {
152 LOG.error(why, e);
153
154
155 }
156
157 @Override
158 public boolean isAborted() {
159 return false;
160 }
161 });
162 }
163
164
165
166
167
168
169
170
171
172
173 @Deprecated
174 public void addPeer(String id, String clusterKey) throws ReplicationException {
175 this.addPeer(id, new ReplicationPeerConfig().setClusterKey(clusterKey), null);
176 }
177
178 @Deprecated
179 public void addPeer(String id, String clusterKey, String tableCFs)
180 throws ReplicationException {
181 this.replicationPeers.addPeer(id,
182 new ReplicationPeerConfig().setClusterKey(clusterKey), tableCFs);
183 }
184
185
186
187
188
189
190
191
192
193
194 public void addPeer(String id, ReplicationPeerConfig peerConfig,
195 Map<TableName, ? extends Collection<String>> tableCfs) throws ReplicationException {
196 this.replicationPeers.addPeer(id, peerConfig, getTableCfsStr(tableCfs));
197 }
198
199 @VisibleForTesting
200 static String getTableCfsStr(Map<TableName, ? extends Collection<String>> tableCfs) {
201 String tableCfsStr = null;
202 if (tableCfs != null) {
203
204 StringBuilder builder = new StringBuilder();
205 for (Entry<TableName, ? extends Collection<String>> entry : tableCfs.entrySet()) {
206 if (builder.length() > 0) {
207 builder.append(";");
208 }
209 builder.append(entry.getKey());
210 if (entry.getValue() != null && !entry.getValue().isEmpty()) {
211 builder.append(":");
212 builder.append(StringUtils.join(entry.getValue(), ","));
213 }
214 }
215 tableCfsStr = builder.toString();
216 }
217 return tableCfsStr;
218 }
219
220 public void updatePeerConfig(String id, ReplicationPeerConfig peerConfig)
221 throws ReplicationException {
222 this.replicationPeers.updatePeerConfig(id, peerConfig);
223 }
224
225
226
227
228 public void removePeer(String id) throws ReplicationException {
229 this.replicationPeers.removePeer(id);
230 }
231
232
233
234
235
236 public void enablePeer(String id) throws ReplicationException {
237 this.replicationPeers.enablePeer(id);
238 }
239
240
241
242
243
244 public void disablePeer(String id) throws ReplicationException {
245 this.replicationPeers.disablePeer(id);
246 }
247
248
249
250
251
252 public int getPeersCount() {
253 return this.replicationPeers.getAllPeerIds().size();
254 }
255
256
257
258
259
260
261 @Deprecated
262 public Map<String, String> listPeers() {
263 Map<String, ReplicationPeerConfig> peers = this.listPeerConfigs();
264 Map<String, String> ret = new HashMap<String, String>(peers.size());
265
266 for (Map.Entry<String, ReplicationPeerConfig> entry : peers.entrySet()) {
267 ret.put(entry.getKey(), entry.getValue().getClusterKey());
268 }
269 return ret;
270 }
271
272 public Map<String, ReplicationPeerConfig> listPeerConfigs() {
273 return this.replicationPeers.getAllPeerConfigs();
274 }
275
276 public ReplicationPeerConfig getPeerConfig(String id) throws ReplicationException {
277 return this.replicationPeers.getReplicationPeerConfig(id);
278 }
279
280
281
282
283
284 public String getPeerTableCFs(String id) throws ReplicationException {
285 return this.replicationPeers.getPeerTableCFsConfig(id);
286 }
287
288
289
290
291
292
293 @Deprecated
294 public void setPeerTableCFs(String id, String tableCFs) throws ReplicationException {
295 this.replicationPeers.setPeerTableCFsConfig(id, tableCFs);
296 }
297
298
299
300
301
302
303
304
305
306 public void setPeerTableCFs(String id, Map<TableName, ? extends Collection<String>> tableCfs)
307 throws ReplicationException {
308 this.replicationPeers.setPeerTableCFsConfig(id, getTableCfsStr(tableCfs));
309 }
310
311
312
313
314
315
316
317 public boolean getPeerState(String id) throws ReplicationException {
318 return this.replicationPeers.getStatusOfPeerFromBackingStore(id);
319 }
320
321 @Override
322 public void close() throws IOException {
323 if (this.zkw != null) {
324 this.zkw.close();
325 }
326 if (this.connection != null) {
327 this.connection.close();
328 }
329 }
330
331
332
333
334
335
336
337
338
339
340
341
342
343 public List<HashMap<String, String>> listReplicated() throws IOException {
344 List<HashMap<String, String>> replicationColFams = new ArrayList<HashMap<String, String>>();
345 HTableDescriptor[] tables = this.connection.listTables();
346
347 for (HTableDescriptor table : tables) {
348 HColumnDescriptor[] columns = table.getColumnFamilies();
349 String tableName = table.getNameAsString();
350 for (HColumnDescriptor column : columns) {
351 if (column.getScope() != HConstants.REPLICATION_SCOPE_LOCAL) {
352
353 HashMap<String, String> replicationEntry = new HashMap<String, String>();
354 replicationEntry.put(TNAME, tableName);
355 replicationEntry.put(CFNAME, column.getNameAsString());
356 replicationEntry.put(REPLICATIONTYPE, REPLICATIONGLOBAL);
357 replicationColFams.add(replicationEntry);
358 }
359 }
360 }
361
362 return replicationColFams;
363 }
364
365
366
367
368
369
370 public void enableTableRep(final TableName tableName) throws IOException {
371 if (tableName == null) {
372 throw new IllegalArgumentException("Table name cannot be null");
373 }
374 HBaseAdmin admin = null;
375 try {
376 admin = new HBaseAdmin(this.connection.getConfiguration());
377 if (!admin.tableExists(tableName)) {
378 throw new TableNotFoundException("Table '" + tableName.getNamespaceAsString()
379 + "' does not exists.");
380 }
381 } finally {
382 try {
383 admin.close();
384 } catch (IOException e) {
385 LOG.warn("Failed to close admin connection.");
386 LOG.debug("Details on failure to close admin connection.", e);
387 }
388 }
389 byte[][] splits = getTableSplitRowKeys(tableName);
390 checkAndSyncTableDescToPeers(tableName, splits);
391 setTableRep(tableName, true);
392 }
393
394
395
396
397
398
399 public void disableTableRep(final TableName tableName) throws IOException {
400 if (tableName == null) {
401 throw new IllegalArgumentException("Table name is null");
402 }
403
404 HBaseAdmin admin = null;
405 try {
406 admin = new HBaseAdmin(this.connection.getConfiguration());
407 if (!admin.tableExists(tableName)) {
408 throw new TableNotFoundException("Table '" + tableName.getNamespaceAsString()
409 + "' does not exists.");
410 }
411 } finally {
412 try {
413 admin.close();
414 } catch (IOException e) {
415 LOG.warn("Failed to close admin connection.");
416 LOG.debug("Details on failure to close admin connection.", e);
417 }
418 }
419 setTableRep(tableName, false);
420 }
421
422
423
424
425
426
427
428 private byte[][] getTableSplitRowKeys(TableName tableName) throws IOException {
429 HTable table = null;
430 try {
431 table = new HTable(this.connection.getConfiguration(), tableName);
432 byte[][] startKeys = table.getStartKeys();
433 if (startKeys.length == 1) {
434 return null;
435 }
436 byte[][] splits = new byte[startKeys.length - 1][];
437 for (int i = 1; i < startKeys.length; i++) {
438 splits[i - 1] = startKeys[i];
439 }
440 return splits;
441 } finally {
442 if (table != null) {
443 try {
444 table.close();
445 } catch (IOException e) {
446 LOG.warn("Unable to close table");
447 }
448 }
449 }
450 }
451
452
453
454
455
456
457
458
459
460
461
462 private void checkAndSyncTableDescToPeers(final TableName tableName, final byte[][] splits)
463 throws IOException {
464 List<ReplicationPeer> repPeers = listReplicationPeers();
465 if (repPeers == null || repPeers.size() <= 0) {
466 throw new IllegalArgumentException("Found no peer cluster for replication.");
467 }
468 for (ReplicationPeer repPeer : repPeers) {
469 Configuration peerConf = repPeer.getConfiguration();
470 HTableDescriptor htd = null;
471 HBaseAdmin repHBaseAdmin = null;
472 try {
473 repHBaseAdmin = new HBaseAdmin(peerConf);
474 htd = this.connection.getHTableDescriptor(tableName);
475 HTableDescriptor peerHtd = null;
476 if (!repHBaseAdmin.tableExists(tableName)) {
477 repHBaseAdmin.createTable(htd, splits);
478 } else {
479 peerHtd = repHBaseAdmin.getTableDescriptor(tableName);
480 if (peerHtd == null) {
481 throw new IllegalArgumentException("Failed to get table descriptor for table "
482 + tableName.getNameAsString() + " from peer cluster " + repPeer.getId());
483 } else if (!peerHtd.equals(htd)) {
484 throw new IllegalArgumentException("Table " + tableName.getNameAsString()
485 + " exists in peer cluster " + repPeer.getId()
486 + ", but the table descriptors are not same when comapred with source cluster."
487 + " Thus can not enable the table's replication switch.");
488 }
489 }
490 } finally {
491 if (repHBaseAdmin != null) {
492 try {
493 repHBaseAdmin.close();
494 } catch (IOException e) {
495 LOG.warn("Failed to close admin connection.");
496 LOG.debug("Details on failure to close admin connection.", e);
497 }
498 }
499 }
500 }
501 }
502
503 @VisibleForTesting
504 public void peerAdded(String id) throws ReplicationException {
505 this.replicationPeers.peerAdded(id);
506 }
507
508 @VisibleForTesting
509 List<ReplicationPeer> listReplicationPeers() {
510 Map<String, ReplicationPeerConfig> peers = listPeerConfigs();
511 if (peers == null || peers.size() <= 0) {
512 return null;
513 }
514 List<ReplicationPeer> listOfPeers = new ArrayList<ReplicationPeer>(peers.size());
515 for (Entry<String, ReplicationPeerConfig> peerEntry : peers.entrySet()) {
516 String peerId = peerEntry.getKey();
517 try {
518 Pair<ReplicationPeerConfig, Configuration> pair = this.replicationPeers.getPeerConf(peerId);
519 Configuration peerConf = pair.getSecond();
520 ReplicationPeer peer = new ReplicationPeerZKImpl(peerConf, peerId, pair.getFirst());
521 listOfPeers.add(peer);
522 } catch (ReplicationException e) {
523 LOG.warn("Failed to get valid replication peers. "
524 + "Error connecting to peer cluster with peerId=" + peerId + ". Error message="
525 + e.getMessage());
526 LOG.debug("Failure details to get valid replication peers.", e);
527 continue;
528 }
529 }
530 return listOfPeers;
531 }
532
533
534
535
536
537
538
539 private void setTableRep(final TableName tableName, boolean isRepEnabled) throws IOException {
540 HBaseAdmin admin = null;
541 try {
542 admin = new HBaseAdmin(this.connection.getConfiguration());
543 HTableDescriptor htd = admin.getTableDescriptor(tableName);
544 if (isTableRepEnabled(htd) ^ isRepEnabled) {
545 boolean isOnlineSchemaUpdateEnabled =
546 this.connection.getConfiguration()
547 .getBoolean("hbase.online.schema.update.enable", true);
548 if (!isOnlineSchemaUpdateEnabled) {
549 admin.disableTable(tableName);
550 }
551 for (HColumnDescriptor hcd : htd.getFamilies()) {
552 hcd.setScope(isRepEnabled ? HConstants.REPLICATION_SCOPE_GLOBAL
553 : HConstants.REPLICATION_SCOPE_LOCAL);
554 }
555 admin.modifyTable(tableName, htd);
556 if (!isOnlineSchemaUpdateEnabled) {
557 admin.enableTable(tableName);
558 }
559 }
560 } finally {
561 if (admin != null) {
562 try {
563 admin.close();
564 } catch (IOException e) {
565 LOG.warn("Failed to close admin connection.");
566 LOG.debug("Details on failure to close admin connection.", e);
567 }
568 }
569 }
570 }
571
572
573
574
575
576 private boolean isTableRepEnabled(HTableDescriptor htd) {
577 for (HColumnDescriptor hcd : htd.getFamilies()) {
578 if (hcd.getScope() != HConstants.REPLICATION_SCOPE_GLOBAL) {
579 return false;
580 }
581 }
582 return true;
583 }
584 }