001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.backup; 019 020import java.io.IOException; 021import java.io.InputStream; 022import java.util.ArrayList; 023import java.util.Calendar; 024import java.util.Date; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028import java.util.Map.Entry; 029import java.util.Set; 030import java.util.function.Predicate; 031import org.apache.commons.lang3.StringUtils; 032import org.apache.hadoop.hbase.TableName; 033import org.apache.hadoop.hbase.backup.util.BackupUtils; 034import org.apache.hadoop.hbase.util.Bytes; 035import org.apache.yetus.audience.InterfaceAudience; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 040import org.apache.hadoop.hbase.shaded.protobuf.generated.BackupProtos; 041 042/** 043 * An object to encapsulate the information for each backup session 044 */ 045@InterfaceAudience.Private 046public class BackupInfo implements Comparable<BackupInfo> { 047 private static final Logger LOG = LoggerFactory.getLogger(BackupInfo.class); 048 private static final int MAX_FAILED_MESSAGE_LENGTH = 1024; 049 050 public interface Filter extends Predicate<BackupInfo> { 051 /** Returns true if the BackupInfo passes the filter, false otherwise */ 052 @Override 053 boolean test(BackupInfo backupInfo); 054 } 055 056 public static Filter withRoot(String backupRoot) { 057 return info -> info.getBackupRootDir().equals(backupRoot); 058 } 059 060 public static Filter withType(BackupType type) { 061 return info -> info.getType() == type; 062 } 063 064 public static Filter withState(BackupState state) { 065 return info -> info.getState() == state; 066 } 067 068 /** 069 * Backup session states 070 */ 071 public enum BackupState { 072 RUNNING, 073 COMPLETE, 074 FAILED 075 } 076 077 /** 078 * BackupPhase - phases of an ACTIVE backup session (running), when state of a backup session is 079 * BackupState.RUNNING 080 */ 081 public enum BackupPhase { 082 REQUEST, 083 SETUP_WAL_REPLICATION, 084 SNAPSHOT, 085 PREPARE_INCREMENTAL, 086 SNAPSHOTCOPY, 087 INCREMENTAL_COPY, 088 STORE_MANIFEST 089 } 090 091 /** 092 * Backup id 093 */ 094 private String backupId; 095 096 /** 097 * Backup type, full or incremental 098 */ 099 private BackupType type; 100 101 /** 102 * Target root directory for storing the backup files 103 */ 104 private String backupRootDir; 105 106 /** 107 * Backup state 108 */ 109 private BackupState state; 110 111 /** 112 * Backup phase 113 */ 114 private BackupPhase phase = BackupPhase.REQUEST; 115 116 /** 117 * Backup failure message 118 */ 119 private String failedMsg; 120 121 /** 122 * Backup status map for all tables 123 */ 124 private Map<TableName, BackupTableInfo> backupTableInfoMap; 125 126 /** 127 * Actual start timestamp of a backup process 128 */ 129 private long startTs; 130 131 /** 132 * Actual end timestamp of the backup process 133 */ 134 private long completeTs; 135 136 /** 137 * Committed WAL timestamp for incremental backup 138 */ 139 private long incrCommittedWalTs; 140 141 /** 142 * Total bytes of incremental logs copied 143 */ 144 private long totalBytesCopied; 145 146 /** 147 * For incremental backup, a location of a backed-up hlogs 148 */ 149 private String hlogTargetDir = null; 150 151 /** 152 * Incremental backup file list 153 */ 154 private List<String> incrBackupFileList; 155 156 /** 157 * New region server log timestamps for table set after distributed log roll. The keys consist of 158 * all tables that are part of the backup chain of the backup root (not just the tables that were 159 * specified when creating the backup, which could be a subset). The value is a map of 160 * RegionServer hostname to the last log-roll timestamp, i.e. the point up to which logs are 161 * included in the backup. 162 */ 163 private Map<TableName, Map<String, Long>> tableSetTimestampMap; 164 165 /** 166 * Previous Region server log timestamps for table set after distributed log roll key - table 167 * name, value - map of RegionServer hostname -> last log rolled timestamp 168 */ 169 private Map<TableName, Map<String, Long>> incrTimestampMap; 170 171 /** 172 * Backup progress in %% (0-100) 173 */ 174 private int progress; 175 176 /** 177 * Number of parallel workers. -1 - system defined 178 */ 179 private int workers = -1; 180 181 /** 182 * Bandwidth per worker in MB per sec. -1 - unlimited 183 */ 184 private long bandwidth = -1; 185 186 /** 187 * Do not verify checksum between source snapshot and exported snapshot 188 */ 189 private boolean noChecksumVerify; 190 191 private boolean continuousBackupEnabled; 192 193 public BackupInfo() { 194 backupTableInfoMap = new HashMap<>(); 195 } 196 197 public BackupInfo(String backupId, BackupType type, TableName[] tables, String targetRootDir) { 198 this(); 199 this.backupId = backupId; 200 this.type = type; 201 this.backupRootDir = targetRootDir; 202 this.addTables(tables); 203 if (type == BackupType.INCREMENTAL) { 204 setHLogTargetDir(BackupUtils.getLogBackupDir(targetRootDir, backupId)); 205 } 206 this.startTs = 0; 207 this.completeTs = 0; 208 this.continuousBackupEnabled = false; 209 } 210 211 public int getWorkers() { 212 return workers; 213 } 214 215 public void setWorkers(int workers) { 216 this.workers = workers; 217 } 218 219 public long getBandwidth() { 220 return bandwidth; 221 } 222 223 public void setBandwidth(long bandwidth) { 224 this.bandwidth = bandwidth; 225 } 226 227 public void setNoChecksumVerify(boolean noChecksumVerify) { 228 this.noChecksumVerify = noChecksumVerify; 229 } 230 231 public boolean getNoChecksumVerify() { 232 return noChecksumVerify; 233 } 234 235 public void setBackupTableInfoMap(Map<TableName, BackupTableInfo> backupTableInfoMap) { 236 this.backupTableInfoMap = backupTableInfoMap; 237 } 238 239 public Map<TableName, Map<String, Long>> getTableSetTimestampMap() { 240 return tableSetTimestampMap; 241 } 242 243 public void setTableSetTimestampMap(Map<TableName, Map<String, Long>> tableSetTimestampMap) { 244 this.tableSetTimestampMap = tableSetTimestampMap; 245 } 246 247 public void setType(BackupType type) { 248 this.type = type; 249 } 250 251 public void setBackupRootDir(String targetRootDir) { 252 this.backupRootDir = targetRootDir; 253 } 254 255 public void setTotalBytesCopied(long totalBytesCopied) { 256 this.totalBytesCopied = totalBytesCopied; 257 } 258 259 /** 260 * Set progress (0-100%) 261 * @param p progress value 262 */ 263 public void setProgress(int p) { 264 this.progress = p; 265 } 266 267 /** 268 * Get current progress 269 */ 270 public int getProgress() { 271 return progress; 272 } 273 274 public String getBackupId() { 275 return backupId; 276 } 277 278 public void setBackupId(String backupId) { 279 this.backupId = backupId; 280 } 281 282 public BackupTableInfo getBackupTableInfo(TableName table) { 283 return this.backupTableInfoMap.get(table); 284 } 285 286 public String getFailedMsg() { 287 return failedMsg; 288 } 289 290 public void setFailedMsg(String failedMsg) { 291 if (failedMsg != null && failedMsg.length() > MAX_FAILED_MESSAGE_LENGTH) { 292 failedMsg = failedMsg.substring(0, MAX_FAILED_MESSAGE_LENGTH); 293 } 294 this.failedMsg = failedMsg; 295 } 296 297 public long getStartTs() { 298 return startTs; 299 } 300 301 public void setStartTs(long startTs) { 302 this.startTs = startTs; 303 } 304 305 public long getCompleteTs() { 306 return completeTs; 307 } 308 309 public void setCompleteTs(long endTs) { 310 this.completeTs = endTs; 311 } 312 313 public long getIncrCommittedWalTs() { 314 return incrCommittedWalTs; 315 } 316 317 public void setIncrCommittedWalTs(long timestamp) { 318 this.incrCommittedWalTs = timestamp; 319 } 320 321 public long getTotalBytesCopied() { 322 return totalBytesCopied; 323 } 324 325 public BackupState getState() { 326 return state; 327 } 328 329 public void setState(BackupState flag) { 330 this.state = flag; 331 } 332 333 public BackupPhase getPhase() { 334 return phase; 335 } 336 337 public void setPhase(BackupPhase phase) { 338 this.phase = phase; 339 } 340 341 public BackupType getType() { 342 return type; 343 } 344 345 public void setSnapshotName(TableName table, String snapshotName) { 346 this.backupTableInfoMap.get(table).setSnapshotName(snapshotName); 347 } 348 349 public String getSnapshotName(TableName table) { 350 return this.backupTableInfoMap.get(table).getSnapshotName(); 351 } 352 353 public List<String> getSnapshotNames() { 354 List<String> snapshotNames = new ArrayList<>(); 355 for (BackupTableInfo backupStatus : this.backupTableInfoMap.values()) { 356 snapshotNames.add(backupStatus.getSnapshotName()); 357 } 358 return snapshotNames; 359 } 360 361 public Set<TableName> getTables() { 362 return this.backupTableInfoMap.keySet(); 363 } 364 365 public List<TableName> getTableNames() { 366 return new ArrayList<>(backupTableInfoMap.keySet()); 367 } 368 369 public void addTables(TableName[] tables) { 370 for (TableName table : tables) { 371 BackupTableInfo backupStatus = new BackupTableInfo(table, this.backupRootDir, this.backupId); 372 this.backupTableInfoMap.put(table, backupStatus); 373 } 374 } 375 376 public void setTables(List<TableName> tables) { 377 this.backupTableInfoMap.clear(); 378 for (TableName table : tables) { 379 BackupTableInfo backupStatus = new BackupTableInfo(table, this.backupRootDir, this.backupId); 380 this.backupTableInfoMap.put(table, backupStatus); 381 } 382 } 383 384 public String getBackupRootDir() { 385 return backupRootDir; 386 } 387 388 public String getTableBackupDir(TableName tableName) { 389 return BackupUtils.getTableBackupDir(backupRootDir, backupId, tableName); 390 } 391 392 public void setHLogTargetDir(String hlogTagetDir) { 393 this.hlogTargetDir = hlogTagetDir; 394 } 395 396 public String getHLogTargetDir() { 397 return hlogTargetDir; 398 } 399 400 public List<String> getIncrBackupFileList() { 401 return incrBackupFileList; 402 } 403 404 public void setIncrBackupFileList(List<String> incrBackupFileList) { 405 this.incrBackupFileList = incrBackupFileList; 406 } 407 408 /** 409 * Set the new region server log timestamps after distributed log roll 410 * @param prevTableSetTimestampMap table timestamp map 411 */ 412 public void setIncrTimestampMap(Map<TableName, Map<String, Long>> prevTableSetTimestampMap) { 413 this.incrTimestampMap = prevTableSetTimestampMap; 414 } 415 416 /** 417 * Get new region server log timestamps after distributed log roll 418 * @return new region server log timestamps 419 */ 420 public Map<TableName, Map<String, Long>> getIncrTimestampMap() { 421 return this.incrTimestampMap; 422 } 423 424 public TableName getTableBySnapshot(String snapshotName) { 425 for (Entry<TableName, BackupTableInfo> entry : this.backupTableInfoMap.entrySet()) { 426 if (snapshotName.equals(entry.getValue().getSnapshotName())) { 427 return entry.getKey(); 428 } 429 } 430 return null; 431 } 432 433 public BackupProtos.BackupInfo toProtosBackupInfo() { 434 BackupProtos.BackupInfo.Builder builder = BackupProtos.BackupInfo.newBuilder(); 435 builder.setBackupId(getBackupId()); 436 setBackupTableInfoMap(builder); 437 setTableSetTimestampMap(builder); 438 builder.setCompleteTs(getCompleteTs()); 439 if (getFailedMsg() != null) { 440 builder.setFailedMessage(getFailedMsg()); 441 } 442 if (getState() != null) { 443 builder.setBackupState(BackupProtos.BackupInfo.BackupState.valueOf(getState().name())); 444 } 445 if (getPhase() != null) { 446 builder.setBackupPhase(BackupProtos.BackupInfo.BackupPhase.valueOf(getPhase().name())); 447 } 448 449 builder.setProgress(getProgress()); 450 builder.setStartTs(getStartTs()); 451 builder.setBackupRootDir(getBackupRootDir()); 452 builder.setBackupType(BackupProtos.BackupType.valueOf(getType().name())); 453 builder.setWorkersNumber(workers); 454 builder.setBandwidth(bandwidth); 455 builder.setTotalBytesCopied(totalBytesCopied); 456 builder.setNoChecksumVerify(noChecksumVerify); 457 if (incrBackupFileList != null) { 458 builder.addAllIncrBackupFileList(incrBackupFileList); 459 } 460 if (incrTimestampMap != null) { 461 for (Entry<TableName, Map<String, Long>> entry : incrTimestampMap.entrySet()) { 462 builder.putIncrTimestampMap(entry.getKey().getNameAsString(), 463 BackupProtos.BackupInfo.RSTimestampMap.newBuilder().putAllRsTimestamp(entry.getValue()) 464 .build()); 465 } 466 } 467 builder.setContinuousBackupEnabled(isContinuousBackupEnabled()); 468 builder.setIncrCommittedWalTs(getIncrCommittedWalTs()); 469 return builder.build(); 470 } 471 472 @Override 473 public int hashCode() { 474 int hash = 33 * type.hashCode() + backupId != null ? backupId.hashCode() : 0; 475 if (backupRootDir != null) { 476 hash = 33 * hash + backupRootDir.hashCode(); 477 } 478 hash = 33 * hash + state.hashCode(); 479 hash = 33 * hash + phase.hashCode(); 480 hash = 33 * hash + (int) (startTs ^ (startTs >>> 32)); 481 hash = 33 * hash + (int) (completeTs ^ (completeTs >>> 32)); 482 hash = 33 * hash + (int) (totalBytesCopied ^ (totalBytesCopied >>> 32)); 483 if (hlogTargetDir != null) { 484 hash = 33 * hash + hlogTargetDir.hashCode(); 485 } 486 return hash; 487 } 488 489 @Override 490 public boolean equals(Object obj) { 491 if (obj instanceof BackupInfo) { 492 BackupInfo other = (BackupInfo) obj; 493 try { 494 return Bytes.equals(toByteArray(), other.toByteArray()); 495 } catch (IOException e) { 496 LOG.error(e.toString(), e); 497 return false; 498 } 499 } else { 500 return false; 501 } 502 } 503 504 @Override 505 public String toString() { 506 return backupId; 507 } 508 509 public byte[] toByteArray() throws IOException { 510 return toProtosBackupInfo().toByteArray(); 511 } 512 513 private void setBackupTableInfoMap(BackupProtos.BackupInfo.Builder builder) { 514 for (Entry<TableName, BackupTableInfo> entry : backupTableInfoMap.entrySet()) { 515 builder.addBackupTableInfo(entry.getValue().toProto()); 516 } 517 } 518 519 private void setTableSetTimestampMap(BackupProtos.BackupInfo.Builder builder) { 520 if (this.getTableSetTimestampMap() != null) { 521 for (Entry<TableName, Map<String, Long>> entry : this.getTableSetTimestampMap().entrySet()) { 522 builder.putTableSetTimestamp(entry.getKey().getNameAsString(), 523 BackupProtos.BackupInfo.RSTimestampMap.newBuilder().putAllRsTimestamp(entry.getValue()) 524 .build()); 525 } 526 } 527 } 528 529 public static BackupInfo fromByteArray(byte[] data) throws IOException { 530 return fromProto(BackupProtos.BackupInfo.parseFrom(data)); 531 } 532 533 public static BackupInfo fromStream(final InputStream stream) throws IOException { 534 return fromProto(BackupProtos.BackupInfo.parseDelimitedFrom(stream)); 535 } 536 537 public static BackupInfo fromProto(BackupProtos.BackupInfo proto) { 538 BackupInfo context = new BackupInfo(); 539 context.setBackupId(proto.getBackupId()); 540 context.setBackupTableInfoMap(toMap(proto.getBackupTableInfoList())); 541 context.setTableSetTimestampMap(getTableSetTimestampMap(proto.getTableSetTimestampMap())); 542 context.setCompleteTs(proto.getCompleteTs()); 543 if (proto.hasFailedMessage()) { 544 context.setFailedMsg(proto.getFailedMessage()); 545 } 546 if (proto.hasBackupState()) { 547 context.setState(BackupInfo.BackupState.valueOf(proto.getBackupState().name())); 548 } 549 550 context 551 .setHLogTargetDir(BackupUtils.getLogBackupDir(proto.getBackupRootDir(), proto.getBackupId())); 552 553 if (proto.hasBackupPhase()) { 554 context.setPhase(BackupPhase.valueOf(proto.getBackupPhase().name())); 555 } 556 if (proto.hasProgress()) { 557 context.setProgress(proto.getProgress()); 558 } 559 context.setStartTs(proto.getStartTs()); 560 context.setBackupRootDir(proto.getBackupRootDir()); 561 context.setType(BackupType.valueOf(proto.getBackupType().name())); 562 context.setWorkers(proto.getWorkersNumber()); 563 context.setBandwidth(proto.getBandwidth()); 564 context.setTotalBytesCopied(proto.getTotalBytesCopied()); 565 context.setNoChecksumVerify(proto.getNoChecksumVerify()); 566 if (proto.getIncrBackupFileListCount() > 0) { 567 context.setIncrBackupFileList(new ArrayList<>(proto.getIncrBackupFileListList())); 568 } 569 if (proto.getIncrTimestampMapCount() > 0) { 570 context.setIncrTimestampMap(getTableSetTimestampMap(proto.getIncrTimestampMapMap())); 571 } 572 context.setContinuousBackupEnabled(proto.getContinuousBackupEnabled()); 573 context.setIncrCommittedWalTs(proto.getIncrCommittedWalTs()); 574 return context; 575 } 576 577 private static Map<TableName, BackupTableInfo> toMap(List<BackupProtos.BackupTableInfo> list) { 578 HashMap<TableName, BackupTableInfo> map = new HashMap<>(); 579 for (BackupProtos.BackupTableInfo tbs : list) { 580 map.put(ProtobufUtil.toTableName(tbs.getTableName()), BackupTableInfo.convert(tbs)); 581 } 582 return map; 583 } 584 585 private static Map<TableName, Map<String, Long>> 586 getTableSetTimestampMap(Map<String, BackupProtos.BackupInfo.RSTimestampMap> map) { 587 Map<TableName, Map<String, Long>> tableSetTimestampMap = new HashMap<>(); 588 for (Entry<String, BackupProtos.BackupInfo.RSTimestampMap> entry : map.entrySet()) { 589 tableSetTimestampMap.put(TableName.valueOf(entry.getKey()), 590 entry.getValue().getRsTimestampMap()); 591 } 592 593 return tableSetTimestampMap; 594 } 595 596 public String getShortDescription() { 597 StringBuilder sb = new StringBuilder(); 598 sb.append("{"); 599 sb.append("ID=" + backupId).append(","); 600 sb.append("Type=" + getType()).append(","); 601 sb.append("IsContinuous=" + isContinuousBackupEnabled()).append(","); 602 sb.append("Tables=" + getTableListAsString()).append(","); 603 sb.append("State=" + getState()).append(","); 604 Calendar cal = Calendar.getInstance(); 605 cal.setTimeInMillis(getStartTs()); 606 Date date = cal.getTime(); 607 sb.append("Start time=" + date).append(","); 608 if (state == BackupState.FAILED) { 609 sb.append("Failed message=" + getFailedMsg()).append(","); 610 } else if (state == BackupState.RUNNING) { 611 sb.append("Phase=" + getPhase()).append(","); 612 } else if (state == BackupState.COMPLETE) { 613 cal = Calendar.getInstance(); 614 cal.setTimeInMillis(getCompleteTs()); 615 date = cal.getTime(); 616 sb.append("End time=" + date).append(","); 617 if (getType() == BackupType.INCREMENTAL) { 618 cal = Calendar.getInstance(); 619 cal.setTimeInMillis(getIncrCommittedWalTs()); 620 date = cal.getTime(); 621 sb.append("Committed WAL time for incremental backup=" + date).append(","); 622 } 623 } 624 sb.append("Progress=" + getProgress() + "%"); 625 sb.append("}"); 626 627 return sb.toString(); 628 } 629 630 public String getStatusAndProgressAsString() { 631 StringBuilder sb = new StringBuilder(); 632 sb.append("id: ").append(getBackupId()).append(" state: ").append(getState()) 633 .append(" progress: ").append(getProgress()); 634 return sb.toString(); 635 } 636 637 public String getTableListAsString() { 638 StringBuilder sb = new StringBuilder(); 639 sb.append("{"); 640 sb.append(StringUtils.join(backupTableInfoMap.keySet(), ",")); 641 sb.append("}"); 642 return sb.toString(); 643 } 644 645 /** 646 * We use only time stamps to compare objects during sort operation 647 */ 648 @Override 649 public int compareTo(BackupInfo o) { 650 Long thisTS = 651 Long.valueOf(this.getBackupId().substring(this.getBackupId().lastIndexOf("_") + 1)); 652 Long otherTS = Long.valueOf(o.getBackupId().substring(o.getBackupId().lastIndexOf("_") + 1)); 653 return thisTS.compareTo(otherTS); 654 } 655 656 public void setContinuousBackupEnabled(boolean continuousBackupEnabled) { 657 this.continuousBackupEnabled = continuousBackupEnabled; 658 } 659 660 public boolean isContinuousBackupEnabled() { 661 return this.continuousBackupEnabled; 662 } 663}