1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.backup.impl;
19
20 import java.io.Closeable;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.Set;
29 import java.util.TreeSet;
30
31 import org.apache.commons.lang.StringUtils;
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.apache.hadoop.conf.Configuration;
35 import org.apache.hadoop.hbase.Cell;
36 import org.apache.hadoop.hbase.CellUtil;
37 import org.apache.hadoop.hbase.HBaseConfiguration;
38 import org.apache.hadoop.hbase.HColumnDescriptor;
39 import org.apache.hadoop.hbase.HConstants;
40 import org.apache.hadoop.hbase.HTableDescriptor;
41 import org.apache.hadoop.hbase.TableName;
42 import org.apache.hadoop.hbase.backup.BackupInfo;
43 import org.apache.hadoop.hbase.backup.BackupInfo.BackupState;
44 import org.apache.hadoop.hbase.backup.util.BackupClientUtil;
45 import org.apache.hadoop.hbase.classification.InterfaceAudience;
46 import org.apache.hadoop.hbase.classification.InterfaceStability;
47 import org.apache.hadoop.hbase.client.Connection;
48 import org.apache.hadoop.hbase.client.Delete;
49 import org.apache.hadoop.hbase.client.Get;
50 import org.apache.hadoop.hbase.client.Put;
51 import org.apache.hadoop.hbase.client.Result;
52 import org.apache.hadoop.hbase.client.ResultScanner;
53 import org.apache.hadoop.hbase.client.Scan;
54 import org.apache.hadoop.hbase.client.Table;
55 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
56 import org.apache.hadoop.hbase.protobuf.generated.BackupProtos;
57
58
59
60
61 @InterfaceAudience.Private
62 @InterfaceStability.Evolving
63 public final class BackupSystemTable implements Closeable {
64
65 static class WALItem {
66 String backupId;
67 String walFile;
68 String backupRoot;
69
70 WALItem(String backupId, String walFile, String backupRoot)
71 {
72 this.backupId = backupId;
73 this.walFile = walFile;
74 this.backupRoot = backupRoot;
75 }
76
77 public String getBackupId() {
78 return backupId;
79 }
80
81 public String getWalFile() {
82 return walFile;
83 }
84
85 public String getBackupRoot() {
86 return backupRoot;
87 }
88
89 public String toString() {
90 return "/"+ backupRoot + "/"+backupId + "/" + walFile;
91 }
92
93 }
94
95 private static final Log LOG = LogFactory.getLog(BackupSystemTable.class);
96 private final static TableName tableName = TableName.BACKUP_TABLE_NAME;
97
98 final static byte[] SESSIONS_FAMILY = "session".getBytes();
99
100 final static byte[] META_FAMILY = "meta".getBytes();
101
102
103 private final Connection connection;
104
105 public BackupSystemTable(Connection conn) throws IOException {
106 this.connection = conn;
107 }
108
109
110 public void close() {
111
112 }
113
114
115
116
117
118
119 public void updateBackupInfo(BackupInfo context) throws IOException {
120
121 if (LOG.isDebugEnabled()) {
122 LOG.debug("update backup status in hbase:backup for: " + context.getBackupId()
123 + " set status=" + context.getState());
124 }
125 try (Table table = connection.getTable(tableName)) {
126 Put put = BackupSystemTableHelper.createPutForBackupContext(context);
127 table.put(put);
128 }
129 }
130
131
132
133
134
135
136
137
138 public void deleteBackupInfo(String backupId) throws IOException {
139
140 if (LOG.isDebugEnabled()) {
141 LOG.debug("delete backup status in hbase:backup for " + backupId);
142 }
143 try (Table table = connection.getTable(tableName)) {
144 Delete del = BackupSystemTableHelper.createDeleteForBackupInfo(backupId);
145 table.delete(del);
146 }
147 }
148
149
150
151
152
153
154
155 public BackupInfo readBackupInfo(String backupId) throws IOException {
156 if (LOG.isDebugEnabled()) {
157 LOG.debug("read backup status from hbase:backup for: " + backupId);
158 }
159
160 try (Table table = connection.getTable(tableName)) {
161 Get get = BackupSystemTableHelper.createGetForBackupContext(backupId);
162 Result res = table.get(get);
163 if(res.isEmpty()){
164 return null;
165 }
166 return BackupSystemTableHelper.resultToBackupInfo(res);
167 }
168 }
169
170
171
172
173
174
175
176
177
178 public String readBackupStartCode(String backupRoot) throws IOException {
179 if (LOG.isDebugEnabled()) {
180 LOG.debug("read backup start code from hbase:backup");
181 }
182 try (Table table = connection.getTable(tableName)) {
183 Get get = BackupSystemTableHelper.createGetForStartCode(backupRoot);
184 Result res = table.get(get);
185 if (res.isEmpty()) {
186 return null;
187 }
188 Cell cell = res.listCells().get(0);
189 byte[] val = CellUtil.cloneValue(cell);
190 if (val.length == 0){
191 return null;
192 }
193 return new String(val);
194 }
195 }
196
197
198
199
200
201
202
203 public void writeBackupStartCode(Long startCode, String backupRoot) throws IOException {
204 if (LOG.isDebugEnabled()) {
205 LOG.debug("write backup start code to hbase:backup " + startCode);
206 }
207 try (Table table = connection.getTable(tableName)) {
208 Put put = BackupSystemTableHelper.createPutForStartCode(startCode.toString(), backupRoot);
209 table.put(put);
210 }
211 }
212
213
214
215
216
217
218
219 public HashMap<String, Long> readRegionServerLastLogRollResult(String backupRoot)
220 throws IOException {
221 if (LOG.isDebugEnabled()) {
222 LOG.debug("read region server last roll log result to hbase:backup");
223 }
224
225 Scan scan = BackupSystemTableHelper.createScanForReadRegionServerLastLogRollResult(backupRoot);
226
227 try (Table table = connection.getTable(tableName);
228 ResultScanner scanner = table.getScanner(scan)) {
229 Result res = null;
230 HashMap<String, Long> rsTimestampMap = new HashMap<String, Long>();
231 while ((res = scanner.next()) != null) {
232 res.advance();
233 Cell cell = res.current();
234 byte[] row = CellUtil.cloneRow(cell);
235 String server =
236 BackupSystemTableHelper.getServerNameForReadRegionServerLastLogRollResult(row);
237 byte[] data = CellUtil.cloneValue(cell);
238 rsTimestampMap.put(server, Long.parseLong(new String(data)));
239 }
240 return rsTimestampMap;
241 }
242 }
243
244
245
246
247
248
249
250
251 public void writeRegionServerLastLogRollResult(String server, Long ts, String backupRoot)
252 throws IOException {
253 if (LOG.isDebugEnabled()) {
254 LOG.debug("write region server last roll log result to hbase:backup");
255 }
256 try (Table table = connection.getTable(tableName)) {
257 Put put =
258 BackupSystemTableHelper.createPutForRegionServerLastLogRollResult(server,ts,backupRoot);
259 table.put(put);
260 }
261 }
262
263
264
265
266
267
268
269 public ArrayList<BackupInfo> getBackupHistory(boolean onlyCompleted) throws IOException {
270 if (LOG.isDebugEnabled()) {
271 LOG.debug("get backup history from hbase:backup");
272 }
273 ArrayList<BackupInfo> list ;
274 BackupState state = onlyCompleted? BackupState.COMPLETE: BackupState.ANY;
275 list = getBackupContexts(state);
276 return BackupClientUtil.sortHistoryListDesc(list);
277 }
278
279 public ArrayList<BackupInfo> getBackupHistory() throws IOException {
280 return getBackupHistory(false);
281 }
282
283
284
285
286
287
288
289 public ArrayList<BackupInfo> getBackupContexts(BackupState status) throws IOException {
290 if (LOG.isDebugEnabled()) {
291 LOG.debug("get backup contexts from hbase:backup");
292 }
293
294 Scan scan = BackupSystemTableHelper.createScanForBackupHistory();
295 ArrayList<BackupInfo> list = new ArrayList<BackupInfo>();
296
297 try (Table table = connection.getTable(tableName);
298 ResultScanner scanner = table.getScanner(scan)) {
299 Result res = null;
300 while ((res = scanner.next()) != null) {
301 res.advance();
302 BackupInfo context = BackupSystemTableHelper.cellToBackupInfo(res.current());
303 if (status != BackupState.ANY && context.getState() != status){
304 continue;
305 }
306 list.add(context);
307 }
308 return list;
309 }
310 }
311
312
313
314
315
316
317
318
319
320
321 public void writeRegionServerLogTimestamp(Set<TableName> tables,
322 HashMap<String, Long> newTimestamps, String backupRoot) throws IOException {
323 if (LOG.isDebugEnabled()) {
324 LOG.debug("write RS log time stamps to hbase:backup for tables ["+
325 StringUtils.join(tables, ",")+"]");
326 }
327 List<Put> puts = new ArrayList<Put>();
328 for (TableName table : tables) {
329 byte[] smapData = toTableServerTimestampProto(table, newTimestamps).toByteArray();
330 Put put =
331 BackupSystemTableHelper.createPutForWriteRegionServerLogTimestamp(table,
332 smapData, backupRoot);
333 puts.add(put);
334 }
335 try (Table table = connection.getTable(tableName)) {
336 table.put(puts);
337 }
338 }
339
340
341
342
343
344
345
346
347
348
349 public HashMap<TableName, HashMap<String, Long>> readLogTimestampMap(String backupRoot)
350 throws IOException {
351 if (LOG.isDebugEnabled()) {
352 LOG.debug("read RS log ts from hbase:backup for root="+ backupRoot);
353 }
354
355 HashMap<TableName, HashMap<String, Long>> tableTimestampMap =
356 new HashMap<TableName, HashMap<String, Long>>();
357
358 Scan scan = BackupSystemTableHelper.createScanForReadLogTimestampMap(backupRoot);
359 try (Table table = connection.getTable(tableName);
360 ResultScanner scanner = table.getScanner(scan)) {
361 Result res = null;
362 while ((res = scanner.next()) != null) {
363 res.advance();
364 Cell cell = res.current();
365 byte[] row = CellUtil.cloneRow(cell);
366 String tabName = BackupSystemTableHelper.getTableNameForReadLogTimestampMap(row);
367 TableName tn = TableName.valueOf(tabName);
368 byte[] data = CellUtil.cloneValue(cell);
369 if (data == null) {
370 throw new IOException("Data of last backup data from hbase:backup "
371 + "is empty. Create a backup first.");
372 }
373 if (data != null && data.length > 0) {
374 HashMap<String, Long> lastBackup =
375 fromTableServerTimestampProto(BackupProtos.TableServerTimestamp.parseFrom(data));
376 tableTimestampMap.put(tn, lastBackup);
377 }
378 }
379 return tableTimestampMap;
380 }
381 }
382
383 private BackupProtos.TableServerTimestamp toTableServerTimestampProto(TableName table,
384 Map<String, Long> map) {
385 BackupProtos.TableServerTimestamp.Builder tstBuilder =
386 BackupProtos.TableServerTimestamp.newBuilder();
387 tstBuilder.setTable(ProtobufUtil.toProtoTableName(table));
388
389 for(Entry<String, Long> entry: map.entrySet()) {
390 BackupProtos.ServerTimestamp.Builder builder = BackupProtos.ServerTimestamp.newBuilder();
391 builder.setServer(entry.getKey());
392 builder.setTimestamp(entry.getValue());
393 tstBuilder.addServerTimestamp(builder.build());
394 }
395
396 return tstBuilder.build();
397 }
398
399 private HashMap<String, Long> fromTableServerTimestampProto(
400 BackupProtos.TableServerTimestamp proto) {
401 HashMap<String, Long> map = new HashMap<String, Long> ();
402 List<BackupProtos.ServerTimestamp> list = proto.getServerTimestampList();
403 for(BackupProtos.ServerTimestamp st: list) {
404 map.put(st.getServer(), st.getTimestamp());
405 }
406 return map;
407 }
408
409
410
411
412
413
414
415 public Set<TableName> getIncrementalBackupTableSet(String backupRoot)
416 throws IOException {
417 if (LOG.isDebugEnabled()) {
418 LOG.debug("get incr backup table set from hbase:backup");
419 }
420 TreeSet<TableName> set = new TreeSet<>();
421
422 try (Table table = connection.getTable(tableName)) {
423 Get get = BackupSystemTableHelper.createGetForIncrBackupTableSet(backupRoot);
424 Result res = table.get(get);
425 if (res.isEmpty()) {
426 return set;
427 }
428 List<Cell> cells = res.listCells();
429 for (Cell cell : cells) {
430
431 set.add(TableName.valueOf(CellUtil.cloneQualifier(cell)));
432 }
433 return set;
434 }
435 }
436
437
438
439
440
441
442
443 public void addIncrementalBackupTableSet(Set<TableName> tables, String backupRoot) throws IOException {
444 if (LOG.isDebugEnabled()) {
445 LOG.debug("Add incremental backup table set to hbase:backup. ROOT="+backupRoot +
446 " tables ["+ StringUtils.join(tables, " ")+"]");
447 for (TableName table : tables) {
448 LOG.debug(table);
449 }
450 }
451 try (Table table = connection.getTable(tableName)) {
452 Put put = BackupSystemTableHelper.createPutForIncrBackupTableSet(tables, backupRoot);
453 table.put(put);
454 }
455 }
456
457
458
459
460
461
462
463
464 public void addWALFiles(List<String> files, String backupId,
465 String backupRoot) throws IOException {
466 if (LOG.isDebugEnabled()) {
467 LOG.debug("add WAL files to hbase:backup: "+backupId +" "+backupRoot+" files ["+
468 StringUtils.join(files, ",")+"]");
469 for(String f: files){
470 LOG.debug("add :"+f);
471 }
472 }
473 try (Table table = connection.getTable(tableName)) {
474 List<Put> puts =
475 BackupSystemTableHelper.createPutsForAddWALFiles(files, backupId, backupRoot);
476 table.put(puts);
477 }
478 }
479
480
481
482
483
484
485 public Iterator<WALItem> getWALFilesIterator(String backupRoot) throws IOException {
486 if (LOG.isDebugEnabled()) {
487 LOG.debug("get WAL files from hbase:backup");
488 }
489 final Table table = connection.getTable(tableName);
490 Scan scan = BackupSystemTableHelper.createScanForGetWALs(backupRoot);
491 final ResultScanner scanner = table.getScanner(scan);
492 final Iterator<Result> it = scanner.iterator();
493 return new Iterator<WALItem>() {
494
495 @Override
496 public boolean hasNext() {
497 boolean next = it.hasNext();
498 if (!next) {
499
500 try {
501 scanner.close();
502 table.close();
503 } catch (IOException e) {
504 LOG.error("Close WAL Iterator", e);
505 }
506 }
507 return next;
508 }
509
510 @Override
511 public WALItem next() {
512 Result next = it.next();
513 List<Cell> cells = next.listCells();
514 byte[] buf = cells.get(0).getValueArray();
515 int len = cells.get(0).getValueLength();
516 int offset = cells.get(0).getValueOffset();
517 String backupId = new String(buf, offset, len);
518 buf = cells.get(1).getValueArray();
519 len = cells.get(1).getValueLength();
520 offset = cells.get(1).getValueOffset();
521 String walFile = new String(buf, offset, len);
522 buf = cells.get(2).getValueArray();
523 len = cells.get(2).getValueLength();
524 offset = cells.get(2).getValueOffset();
525 String backupRoot = new String(buf, offset, len);
526 return new WALItem(backupId, walFile, backupRoot);
527 }
528
529 @Override
530 public void remove() {
531
532 throw new RuntimeException("remove is not supported");
533 }
534 };
535
536 }
537
538
539
540
541
542
543
544
545 public boolean isWALFileDeletable(String file) throws IOException {
546 if (LOG.isDebugEnabled()) {
547 LOG.debug("Check if WAL file has been already backed up in hbase:backup "+ file);
548 }
549 try (Table table = connection.getTable(tableName)) {
550 Get get = BackupSystemTableHelper.createGetForCheckWALFile(file);
551 Result res = table.get(get);
552 if (res.isEmpty()){
553 return false;
554 }
555 return true;
556 }
557 }
558
559
560
561
562
563
564
565 public boolean hasBackupSessions() throws IOException {
566 if (LOG.isDebugEnabled()) {
567 LOG.debug("Has backup sessions from hbase:backup");
568 }
569 boolean result = false;
570 Scan scan = BackupSystemTableHelper.createScanForBackupHistory();
571 scan.setCaching(1);
572 try (Table table = connection.getTable(tableName);
573 ResultScanner scanner = table.getScanner(scan)) {
574 if (scanner.next() != null) {
575 result = true;
576 }
577 return result;
578 }
579 }
580
581
582
583
584
585
586
587
588
589
590 public List<String> listBackupSets() throws IOException {
591 if (LOG.isDebugEnabled()) {
592 LOG.debug(" Backup set list");
593 }
594 List<String> list = new ArrayList<String>();
595 Table table = null;
596 ResultScanner scanner = null;
597 try {
598 table = connection.getTable(tableName);
599 Scan scan = BackupSystemTableHelper.createScanForBackupSetList();
600 scan.setMaxVersions(1);
601 scanner = table.getScanner(scan);
602 Result res = null;
603 while ((res = scanner.next()) != null) {
604 res.advance();
605 list.add(BackupSystemTableHelper.cellKeyToBackupSetName(res.current()));
606 }
607 return list;
608 } finally {
609 if(scanner != null) {
610 scanner.close();
611 }
612 if (table != null) {
613 table.close();
614 }
615 }
616 }
617
618
619
620
621
622
623
624 public List<TableName> describeBackupSet(String name) throws IOException {
625 if (LOG.isDebugEnabled()) {
626 LOG.debug(" Backup set describe: "+name);
627 }
628 Table table = null;
629 try {
630 table = connection.getTable(tableName);
631 Get get = BackupSystemTableHelper.createGetForBackupSet(name);
632 Result res = table.get(get);
633 if(res.isEmpty()) return null;
634 res.advance();
635 String[] tables =
636 BackupSystemTableHelper.cellValueToBackupSet(res.current());
637 return toList(tables);
638 } finally {
639 if (table != null) {
640 table.close();
641 }
642 }
643 }
644
645 private List<TableName> toList(String[] tables)
646 {
647 List<TableName> list = new ArrayList<TableName>(tables.length);
648 for(String name: tables) {
649 list.add(TableName.valueOf(name));
650 }
651 return list;
652 }
653
654
655
656
657
658
659
660 public void addToBackupSet(String name, String[] newTables) throws IOException {
661 if (LOG.isDebugEnabled()) {
662 LOG.debug("Backup set add: "+name+" tables ["+ StringUtils.join(newTables, " ")+"]");
663 }
664 Table table = null;
665 String[] union = null;
666 try {
667 table = connection.getTable(tableName);
668 Get get = BackupSystemTableHelper.createGetForBackupSet(name);
669 Result res = table.get(get);
670 if(res.isEmpty()) {
671 union = newTables;
672 } else {
673 res.advance();
674 String[] tables =
675 BackupSystemTableHelper.cellValueToBackupSet(res.current());
676 union = merge(tables, newTables);
677 }
678 Put put = BackupSystemTableHelper.createPutForBackupSet(name, union);
679 table.put(put);
680 } finally {
681 if (table != null) {
682 table.close();
683 }
684 }
685 }
686
687 private String[] merge(String[] tables, String[] newTables) {
688 List<String> list = new ArrayList<String>();
689
690 for(String t: tables){
691 list.add(t);
692 }
693 for(String nt: newTables){
694 if(list.contains(nt)) continue;
695 list.add(nt);
696 }
697 String[] arr = new String[list.size()];
698 list.toArray(arr);
699 return arr;
700 }
701
702
703
704
705
706
707
708 public void removeFromBackupSet(String name, String[] toRemove) throws IOException {
709 if (LOG.isDebugEnabled()) {
710 LOG.debug(" Backup set remove from : " + name+" tables ["+
711 StringUtils.join(toRemove, " ")+"]");
712 }
713 Table table = null;
714 String[] disjoint = null;
715 try {
716 table = connection.getTable(tableName);
717 Get get = BackupSystemTableHelper.createGetForBackupSet(name);
718 Result res = table.get(get);
719 if (res.isEmpty()) {
720 LOG.warn("Backup set '"+ name+"' not found.");
721 return;
722 } else {
723 res.advance();
724 String[] tables = BackupSystemTableHelper.cellValueToBackupSet(res.current());
725 disjoint = disjoin(tables, toRemove);
726 }
727 if (disjoint.length > 0) {
728 Put put = BackupSystemTableHelper.createPutForBackupSet(name, disjoint);
729 table.put(put);
730 } else {
731
732
733 LOG.warn("Backup set '"+ name+"' does not contain tables ["+
734 StringUtils.join(toRemove, " ")+"]");
735 }
736 } finally {
737 if (table != null) {
738 table.close();
739 }
740 }
741 }
742
743 private String[] disjoin(String[] tables, String[] toRemove) {
744 List<String> list = new ArrayList<String>();
745
746 for (String t : tables) {
747 list.add(t);
748 }
749 for (String nt : toRemove) {
750 if (list.contains(nt)) {
751 list.remove(nt);
752 }
753 }
754 String[] arr = new String[list.size()];
755 list.toArray(arr);
756 return arr;
757 }
758
759
760
761
762
763
764 public void deleteBackupSet(String name) throws IOException {
765 if (LOG.isDebugEnabled()) {
766 LOG.debug(" Backup set delete: " + name);
767 }
768 Table table = null;
769 try {
770 table = connection.getTable(tableName);
771 Delete del = BackupSystemTableHelper.createDeleteForBackupSet(name);
772 table.delete(del);
773 } finally {
774 if (table != null) {
775 table.close();
776 }
777 }
778 }
779
780
781
782
783
784 public static HTableDescriptor getSystemTableDescriptor() {
785 HTableDescriptor tableDesc = new HTableDescriptor(tableName);
786 HColumnDescriptor colSessionsDesc = new HColumnDescriptor(SESSIONS_FAMILY);
787 colSessionsDesc.setMaxVersions(1);
788
789 Configuration config = HBaseConfiguration.create();
790 int ttl =
791 config.getInt(HConstants.BACKUP_SYSTEM_TTL_KEY, HConstants.BACKUP_SYSTEM_TTL_DEFAULT);
792 colSessionsDesc.setTimeToLive(ttl);
793 tableDesc.addFamily(colSessionsDesc);
794 HColumnDescriptor colMetaDesc = new HColumnDescriptor(META_FAMILY);
795
796 tableDesc.addFamily(colMetaDesc);
797 return tableDesc;
798 }
799
800 public static String getTableNameAsString() {
801 return tableName.getNameAsString();
802 }
803
804 public static TableName getTableName() {
805 return tableName;
806 }
807 }