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
221
222
223
224 public void removePeer(String id) throws ReplicationException {
225 this.replicationPeers.removePeer(id);
226 }
227
228
229
230
231
232 public void enablePeer(String id) throws ReplicationException {
233 this.replicationPeers.enablePeer(id);
234 }
235
236
237
238
239
240 public void disablePeer(String id) throws ReplicationException {
241 this.replicationPeers.disablePeer(id);
242 }
243
244
245
246
247
248 public int getPeersCount() {
249 return this.replicationPeers.getAllPeerIds().size();
250 }
251
252
253
254
255
256
257 @Deprecated
258 public Map<String, String> listPeers() {
259 Map<String, ReplicationPeerConfig> peers = this.listPeerConfigs();
260 Map<String, String> ret = new HashMap<String, String>(peers.size());
261
262 for (Map.Entry<String, ReplicationPeerConfig> entry : peers.entrySet()) {
263 ret.put(entry.getKey(), entry.getValue().getClusterKey());
264 }
265 return ret;
266 }
267
268 public Map<String, ReplicationPeerConfig> listPeerConfigs() {
269 return this.replicationPeers.getAllPeerConfigs();
270 }
271
272 public ReplicationPeerConfig getPeerConfig(String id) throws ReplicationException {
273 return this.replicationPeers.getReplicationPeerConfig(id);
274 }
275
276
277
278
279
280 public String getPeerTableCFs(String id) throws ReplicationException {
281 return this.replicationPeers.getPeerTableCFsConfig(id);
282 }
283
284
285
286
287
288
289 @Deprecated
290 public void setPeerTableCFs(String id, String tableCFs) throws ReplicationException {
291 this.replicationPeers.setPeerTableCFsConfig(id, tableCFs);
292 }
293
294
295
296
297
298
299
300
301
302 public void setPeerTableCFs(String id, Map<TableName, ? extends Collection<String>> tableCfs)
303 throws ReplicationException {
304 this.replicationPeers.setPeerTableCFsConfig(id, getTableCfsStr(tableCfs));
305 }
306
307
308
309
310
311
312
313 public boolean getPeerState(String id) throws ReplicationException {
314 return this.replicationPeers.getStatusOfPeerFromBackingStore(id);
315 }
316
317 @Override
318 public void close() throws IOException {
319 if (this.zkw != null) {
320 this.zkw.close();
321 }
322 if (this.connection != null) {
323 this.connection.close();
324 }
325 }
326
327
328
329
330
331
332
333
334
335
336
337
338
339 public List<HashMap<String, String>> listReplicated() throws IOException {
340 List<HashMap<String, String>> replicationColFams = new ArrayList<HashMap<String, String>>();
341 HTableDescriptor[] tables = this.connection.listTables();
342
343 for (HTableDescriptor table : tables) {
344 HColumnDescriptor[] columns = table.getColumnFamilies();
345 String tableName = table.getNameAsString();
346 for (HColumnDescriptor column : columns) {
347 if (column.getScope() != HConstants.REPLICATION_SCOPE_LOCAL) {
348
349 HashMap<String, String> replicationEntry = new HashMap<String, String>();
350 replicationEntry.put(TNAME, tableName);
351 replicationEntry.put(CFNAME, column.getNameAsString());
352 replicationEntry.put(REPLICATIONTYPE, REPLICATIONGLOBAL);
353 replicationColFams.add(replicationEntry);
354 }
355 }
356 }
357
358 return replicationColFams;
359 }
360
361
362
363
364
365
366 public void enableTableRep(final TableName tableName) throws IOException {
367 if (tableName == null) {
368 throw new IllegalArgumentException("Table name cannot be null");
369 }
370 HBaseAdmin admin = null;
371 try {
372 admin = new HBaseAdmin(this.connection.getConfiguration());
373 if (!admin.tableExists(tableName)) {
374 throw new TableNotFoundException("Table '" + tableName.getNamespaceAsString()
375 + "' does not exists.");
376 }
377 } finally {
378 try {
379 admin.close();
380 } catch (IOException e) {
381 LOG.warn("Failed to close admin connection.");
382 LOG.debug("Details on failure to close admin connection.", e);
383 }
384 }
385 byte[][] splits = getTableSplitRowKeys(tableName);
386 checkAndSyncTableDescToPeers(tableName, splits);
387 setTableRep(tableName, true);
388 }
389
390
391
392
393
394
395 public void disableTableRep(final TableName tableName) throws IOException {
396 if (tableName == null) {
397 throw new IllegalArgumentException("Table name is null");
398 }
399
400 HBaseAdmin admin = null;
401 try {
402 admin = new HBaseAdmin(this.connection.getConfiguration());
403 if (!admin.tableExists(tableName)) {
404 throw new TableNotFoundException("Table '" + tableName.getNamespaceAsString()
405 + "' does not exists.");
406 }
407 } finally {
408 try {
409 admin.close();
410 } catch (IOException e) {
411 LOG.warn("Failed to close admin connection.");
412 LOG.debug("Details on failure to close admin connection.", e);
413 }
414 }
415 setTableRep(tableName, false);
416 }
417
418
419
420
421
422
423
424 private byte[][] getTableSplitRowKeys(TableName tableName) throws IOException {
425 HTable table = null;
426 try {
427 table = new HTable(this.connection.getConfiguration(), tableName);
428 byte[][] startKeys = table.getStartKeys();
429 if (startKeys.length == 1) {
430 return null;
431 }
432 byte[][] splits = new byte[startKeys.length - 1][];
433 for (int i = 1; i < startKeys.length; i++) {
434 splits[i - 1] = startKeys[i];
435 }
436 return splits;
437 } finally {
438 if (table != null) {
439 try {
440 table.close();
441 } catch (IOException e) {
442 LOG.warn("Unable to close table");
443 }
444 }
445 }
446 }
447
448
449
450
451
452
453
454
455
456
457
458 private void checkAndSyncTableDescToPeers(final TableName tableName, final byte[][] splits)
459 throws IOException {
460 List<ReplicationPeer> repPeers = listReplicationPeers();
461 if (repPeers == null || repPeers.size() <= 0) {
462 throw new IllegalArgumentException("Found no peer cluster for replication.");
463 }
464 for (ReplicationPeer repPeer : repPeers) {
465 Configuration peerConf = repPeer.getConfiguration();
466 HTableDescriptor htd = null;
467 HBaseAdmin repHBaseAdmin = null;
468 try {
469 repHBaseAdmin = new HBaseAdmin(peerConf);
470 htd = this.connection.getHTableDescriptor(tableName);
471 HTableDescriptor peerHtd = null;
472 if (!repHBaseAdmin.tableExists(tableName)) {
473 repHBaseAdmin.createTable(htd, splits);
474 } else {
475 peerHtd = repHBaseAdmin.getTableDescriptor(tableName);
476 if (peerHtd == null) {
477 throw new IllegalArgumentException("Failed to get table descriptor for table "
478 + tableName.getNameAsString() + " from peer cluster " + repPeer.getId());
479 } else if (!peerHtd.equals(htd)) {
480 throw new IllegalArgumentException("Table " + tableName.getNameAsString()
481 + " exists in peer cluster " + repPeer.getId()
482 + ", but the table descriptors are not same when comapred with source cluster."
483 + " Thus can not enable the table's replication switch.");
484 }
485 }
486 } finally {
487 if (repHBaseAdmin != null) {
488 try {
489 repHBaseAdmin.close();
490 } catch (IOException e) {
491 LOG.warn("Failed to close admin connection.");
492 LOG.debug("Details on failure to close admin connection.", e);
493 }
494 }
495 }
496 }
497 }
498
499 @VisibleForTesting
500 List<ReplicationPeer> listReplicationPeers() {
501 Map<String, ReplicationPeerConfig> peers = listPeerConfigs();
502 if (peers == null || peers.size() <= 0) {
503 return null;
504 }
505 List<ReplicationPeer> listOfPeers = new ArrayList<ReplicationPeer>(peers.size());
506 for (Entry<String, ReplicationPeerConfig> peerEntry : peers.entrySet()) {
507 String peerId = peerEntry.getKey();
508 try {
509 Pair<ReplicationPeerConfig, Configuration> pair = this.replicationPeers.getPeerConf(peerId);
510 Configuration peerConf = pair.getSecond();
511 ReplicationPeer peer = new ReplicationPeerZKImpl(peerConf, peerId, pair.getFirst());
512 listOfPeers.add(peer);
513 } catch (ReplicationException e) {
514 LOG.warn("Failed to get valid replication peers. "
515 + "Error connecting to peer cluster with peerId=" + peerId + ". Error message="
516 + e.getMessage());
517 LOG.debug("Failure details to get valid replication peers.", e);
518 continue;
519 }
520 }
521 return listOfPeers;
522 }
523
524
525
526
527
528
529
530 private void setTableRep(final TableName tableName, boolean isRepEnabled) throws IOException {
531 HBaseAdmin admin = null;
532 try {
533 admin = new HBaseAdmin(this.connection.getConfiguration());
534 HTableDescriptor htd = admin.getTableDescriptor(tableName);
535 if (isTableRepEnabled(htd) ^ isRepEnabled) {
536 boolean isOnlineSchemaUpdateEnabled =
537 this.connection.getConfiguration()
538 .getBoolean("hbase.online.schema.update.enable", true);
539 if (!isOnlineSchemaUpdateEnabled) {
540 admin.disableTable(tableName);
541 }
542 for (HColumnDescriptor hcd : htd.getFamilies()) {
543 hcd.setScope(isRepEnabled ? HConstants.REPLICATION_SCOPE_GLOBAL
544 : HConstants.REPLICATION_SCOPE_LOCAL);
545 }
546 admin.modifyTable(tableName, htd);
547 if (!isOnlineSchemaUpdateEnabled) {
548 admin.enableTable(tableName);
549 }
550 }
551 } finally {
552 if (admin != null) {
553 try {
554 admin.close();
555 } catch (IOException e) {
556 LOG.warn("Failed to close admin connection.");
557 LOG.debug("Details on failure to close admin connection.", e);
558 }
559 }
560 }
561 }
562
563
564
565
566
567 private boolean isTableRepEnabled(HTableDescriptor htd) {
568 for (HColumnDescriptor hcd : htd.getFamilies()) {
569 if (hcd.getScope() != HConstants.REPLICATION_SCOPE_GLOBAL) {
570 return false;
571 }
572 }
573 return true;
574 }
575 }