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 SNAPSHOT, 084 PREPARE_INCREMENTAL, 085 SNAPSHOTCOPY, 086 INCREMENTAL_COPY, 087 STORE_MANIFEST 088 } 089 090 /** 091 * Backup id 092 */ 093 private String backupId; 094 095 /** 096 * Backup type, full or incremental 097 */ 098 private BackupType type; 099 100 /** 101 * Target root directory for storing the backup files 102 */ 103 private String backupRootDir; 104 105 /** 106 * Backup state 107 */ 108 private BackupState state; 109 110 /** 111 * Backup phase 112 */ 113 private BackupPhase phase = BackupPhase.REQUEST; 114 115 /** 116 * Backup failure message 117 */ 118 private String failedMsg; 119 120 /** 121 * Backup status map for all tables 122 */ 123 private Map<TableName, BackupTableInfo> backupTableInfoMap; 124 125 /** 126 * Actual start timestamp of a backup process 127 */ 128 private long startTs; 129 130 /** 131 * Actual end timestamp of the backup process 132 */ 133 private long completeTs; 134 135 /** 136 * Total bytes of incremental logs copied 137 */ 138 private long totalBytesCopied; 139 140 /** 141 * For incremental backup, a location of a backed-up hlogs 142 */ 143 private String hlogTargetDir = null; 144 145 /** 146 * Incremental backup file list 147 */ 148 private List<String> incrBackupFileList; 149 150 /** 151 * New region server log timestamps for table set after distributed log roll. The keys consist of 152 * all tables that are part of the backup chain of the backup root (not just the tables that were 153 * specified when creating the backup, which could be a subset). The value is a map of 154 * RegionServer hostname to the last log-roll timestamp, i.e. the point up to which logs are 155 * included in the backup. 156 */ 157 private Map<TableName, Map<String, Long>> tableSetTimestampMap; 158 159 /** 160 * Previous Region server log timestamps for table set after distributed log roll key - table 161 * name, value - map of RegionServer hostname -> last log rolled timestamp 162 */ 163 private Map<TableName, Map<String, Long>> incrTimestampMap; 164 165 /** 166 * Backup progress in %% (0-100) 167 */ 168 private int progress; 169 170 /** 171 * Number of parallel workers. -1 - system defined 172 */ 173 private int workers = -1; 174 175 /** 176 * Bandwidth per worker in MB per sec. -1 - unlimited 177 */ 178 private long bandwidth = -1; 179 180 /** 181 * Do not verify checksum between source snapshot and exported snapshot 182 */ 183 private boolean noChecksumVerify; 184 185 public BackupInfo() { 186 backupTableInfoMap = new HashMap<>(); 187 } 188 189 public BackupInfo(String backupId, BackupType type, TableName[] tables, String targetRootDir) { 190 this(); 191 this.backupId = backupId; 192 this.type = type; 193 this.backupRootDir = targetRootDir; 194 this.addTables(tables); 195 if (type == BackupType.INCREMENTAL) { 196 setHLogTargetDir(BackupUtils.getLogBackupDir(targetRootDir, backupId)); 197 } 198 this.startTs = 0; 199 this.completeTs = 0; 200 } 201 202 public int getWorkers() { 203 return workers; 204 } 205 206 public void setWorkers(int workers) { 207 this.workers = workers; 208 } 209 210 public long getBandwidth() { 211 return bandwidth; 212 } 213 214 public void setBandwidth(long bandwidth) { 215 this.bandwidth = bandwidth; 216 } 217 218 public void setNoChecksumVerify(boolean noChecksumVerify) { 219 this.noChecksumVerify = noChecksumVerify; 220 } 221 222 public boolean getNoChecksumVerify() { 223 return noChecksumVerify; 224 } 225 226 public void setBackupTableInfoMap(Map<TableName, BackupTableInfo> backupTableInfoMap) { 227 this.backupTableInfoMap = backupTableInfoMap; 228 } 229 230 public Map<TableName, Map<String, Long>> getTableSetTimestampMap() { 231 return tableSetTimestampMap; 232 } 233 234 public void setTableSetTimestampMap(Map<TableName, Map<String, Long>> tableSetTimestampMap) { 235 this.tableSetTimestampMap = tableSetTimestampMap; 236 } 237 238 public void setType(BackupType type) { 239 this.type = type; 240 } 241 242 public void setBackupRootDir(String targetRootDir) { 243 this.backupRootDir = targetRootDir; 244 } 245 246 public void setTotalBytesCopied(long totalBytesCopied) { 247 this.totalBytesCopied = totalBytesCopied; 248 } 249 250 /** 251 * Set progress (0-100%) 252 * @param p progress value 253 */ 254 public void setProgress(int p) { 255 this.progress = p; 256 } 257 258 /** 259 * Get current progress 260 */ 261 public int getProgress() { 262 return progress; 263 } 264 265 public String getBackupId() { 266 return backupId; 267 } 268 269 public void setBackupId(String backupId) { 270 this.backupId = backupId; 271 } 272 273 public BackupTableInfo getBackupTableInfo(TableName table) { 274 return this.backupTableInfoMap.get(table); 275 } 276 277 public String getFailedMsg() { 278 return failedMsg; 279 } 280 281 public void setFailedMsg(String failedMsg) { 282 if (failedMsg != null && failedMsg.length() > MAX_FAILED_MESSAGE_LENGTH) { 283 failedMsg = failedMsg.substring(0, MAX_FAILED_MESSAGE_LENGTH); 284 } 285 this.failedMsg = failedMsg; 286 } 287 288 public long getStartTs() { 289 return startTs; 290 } 291 292 public void setStartTs(long startTs) { 293 this.startTs = startTs; 294 } 295 296 public long getCompleteTs() { 297 return completeTs; 298 } 299 300 public void setCompleteTs(long endTs) { 301 this.completeTs = endTs; 302 } 303 304 public long getTotalBytesCopied() { 305 return totalBytesCopied; 306 } 307 308 public BackupState getState() { 309 return state; 310 } 311 312 public void setState(BackupState flag) { 313 this.state = flag; 314 } 315 316 public BackupPhase getPhase() { 317 return phase; 318 } 319 320 public void setPhase(BackupPhase phase) { 321 this.phase = phase; 322 } 323 324 public BackupType getType() { 325 return type; 326 } 327 328 public void setSnapshotName(TableName table, String snapshotName) { 329 this.backupTableInfoMap.get(table).setSnapshotName(snapshotName); 330 } 331 332 public String getSnapshotName(TableName table) { 333 return this.backupTableInfoMap.get(table).getSnapshotName(); 334 } 335 336 public List<String> getSnapshotNames() { 337 List<String> snapshotNames = new ArrayList<>(); 338 for (BackupTableInfo backupStatus : this.backupTableInfoMap.values()) { 339 snapshotNames.add(backupStatus.getSnapshotName()); 340 } 341 return snapshotNames; 342 } 343 344 public Set<TableName> getTables() { 345 return this.backupTableInfoMap.keySet(); 346 } 347 348 public List<TableName> getTableNames() { 349 return new ArrayList<>(backupTableInfoMap.keySet()); 350 } 351 352 public void addTables(TableName[] tables) { 353 for (TableName table : tables) { 354 BackupTableInfo backupStatus = new BackupTableInfo(table, this.backupRootDir, this.backupId); 355 this.backupTableInfoMap.put(table, backupStatus); 356 } 357 } 358 359 public void setTables(List<TableName> tables) { 360 this.backupTableInfoMap.clear(); 361 for (TableName table : tables) { 362 BackupTableInfo backupStatus = new BackupTableInfo(table, this.backupRootDir, this.backupId); 363 this.backupTableInfoMap.put(table, backupStatus); 364 } 365 } 366 367 public String getBackupRootDir() { 368 return backupRootDir; 369 } 370 371 public String getTableBackupDir(TableName tableName) { 372 return BackupUtils.getTableBackupDir(backupRootDir, backupId, tableName); 373 } 374 375 public void setHLogTargetDir(String hlogTagetDir) { 376 this.hlogTargetDir = hlogTagetDir; 377 } 378 379 public String getHLogTargetDir() { 380 return hlogTargetDir; 381 } 382 383 public List<String> getIncrBackupFileList() { 384 return incrBackupFileList; 385 } 386 387 public void setIncrBackupFileList(List<String> incrBackupFileList) { 388 this.incrBackupFileList = incrBackupFileList; 389 } 390 391 /** 392 * Set the new region server log timestamps after distributed log roll 393 * @param prevTableSetTimestampMap table timestamp map 394 */ 395 public void setIncrTimestampMap(Map<TableName, Map<String, Long>> prevTableSetTimestampMap) { 396 this.incrTimestampMap = prevTableSetTimestampMap; 397 } 398 399 /** 400 * Get new region server log timestamps after distributed log roll 401 * @return new region server log timestamps 402 */ 403 public Map<TableName, Map<String, Long>> getIncrTimestampMap() { 404 return this.incrTimestampMap; 405 } 406 407 public TableName getTableBySnapshot(String snapshotName) { 408 for (Entry<TableName, BackupTableInfo> entry : this.backupTableInfoMap.entrySet()) { 409 if (snapshotName.equals(entry.getValue().getSnapshotName())) { 410 return entry.getKey(); 411 } 412 } 413 return null; 414 } 415 416 public BackupProtos.BackupInfo toProtosBackupInfo() { 417 BackupProtos.BackupInfo.Builder builder = BackupProtos.BackupInfo.newBuilder(); 418 builder.setBackupId(getBackupId()); 419 setBackupTableInfoMap(builder); 420 setTableSetTimestampMap(builder); 421 builder.setCompleteTs(getCompleteTs()); 422 if (getFailedMsg() != null) { 423 builder.setFailedMessage(getFailedMsg()); 424 } 425 if (getState() != null) { 426 builder.setBackupState(BackupProtos.BackupInfo.BackupState.valueOf(getState().name())); 427 } 428 if (getPhase() != null) { 429 builder.setBackupPhase(BackupProtos.BackupInfo.BackupPhase.valueOf(getPhase().name())); 430 } 431 432 builder.setProgress(getProgress()); 433 builder.setStartTs(getStartTs()); 434 builder.setBackupRootDir(getBackupRootDir()); 435 builder.setBackupType(BackupProtos.BackupType.valueOf(getType().name())); 436 builder.setWorkersNumber(workers); 437 builder.setBandwidth(bandwidth); 438 builder.setTotalBytesCopied(totalBytesCopied); 439 builder.setNoChecksumVerify(noChecksumVerify); 440 if (incrBackupFileList != null) { 441 builder.addAllIncrBackupFileList(incrBackupFileList); 442 } 443 if (incrTimestampMap != null) { 444 for (Entry<TableName, Map<String, Long>> entry : incrTimestampMap.entrySet()) { 445 builder.putIncrTimestampMap(entry.getKey().getNameAsString(), 446 BackupProtos.BackupInfo.RSTimestampMap.newBuilder().putAllRsTimestamp(entry.getValue()) 447 .build()); 448 } 449 } 450 return builder.build(); 451 } 452 453 @Override 454 public int hashCode() { 455 int hash = 33 * type.hashCode() + backupId != null ? backupId.hashCode() : 0; 456 if (backupRootDir != null) { 457 hash = 33 * hash + backupRootDir.hashCode(); 458 } 459 hash = 33 * hash + state.hashCode(); 460 hash = 33 * hash + phase.hashCode(); 461 hash = 33 * hash + (int) (startTs ^ (startTs >>> 32)); 462 hash = 33 * hash + (int) (completeTs ^ (completeTs >>> 32)); 463 hash = 33 * hash + (int) (totalBytesCopied ^ (totalBytesCopied >>> 32)); 464 if (hlogTargetDir != null) { 465 hash = 33 * hash + hlogTargetDir.hashCode(); 466 } 467 return hash; 468 } 469 470 @Override 471 public boolean equals(Object obj) { 472 if (obj instanceof BackupInfo) { 473 BackupInfo other = (BackupInfo) obj; 474 try { 475 return Bytes.equals(toByteArray(), other.toByteArray()); 476 } catch (IOException e) { 477 LOG.error(e.toString(), e); 478 return false; 479 } 480 } else { 481 return false; 482 } 483 } 484 485 @Override 486 public String toString() { 487 return backupId; 488 } 489 490 public byte[] toByteArray() throws IOException { 491 return toProtosBackupInfo().toByteArray(); 492 } 493 494 private void setBackupTableInfoMap(BackupProtos.BackupInfo.Builder builder) { 495 for (Entry<TableName, BackupTableInfo> entry : backupTableInfoMap.entrySet()) { 496 builder.addBackupTableInfo(entry.getValue().toProto()); 497 } 498 } 499 500 private void setTableSetTimestampMap(BackupProtos.BackupInfo.Builder builder) { 501 if (this.getTableSetTimestampMap() != null) { 502 for (Entry<TableName, Map<String, Long>> entry : this.getTableSetTimestampMap().entrySet()) { 503 builder.putTableSetTimestamp(entry.getKey().getNameAsString(), 504 BackupProtos.BackupInfo.RSTimestampMap.newBuilder().putAllRsTimestamp(entry.getValue()) 505 .build()); 506 } 507 } 508 } 509 510 public static BackupInfo fromByteArray(byte[] data) throws IOException { 511 return fromProto(BackupProtos.BackupInfo.parseFrom(data)); 512 } 513 514 public static BackupInfo fromStream(final InputStream stream) throws IOException { 515 return fromProto(BackupProtos.BackupInfo.parseDelimitedFrom(stream)); 516 } 517 518 public static BackupInfo fromProto(BackupProtos.BackupInfo proto) { 519 BackupInfo context = new BackupInfo(); 520 context.setBackupId(proto.getBackupId()); 521 context.setBackupTableInfoMap(toMap(proto.getBackupTableInfoList())); 522 context.setTableSetTimestampMap(getTableSetTimestampMap(proto.getTableSetTimestampMap())); 523 context.setCompleteTs(proto.getCompleteTs()); 524 if (proto.hasFailedMessage()) { 525 context.setFailedMsg(proto.getFailedMessage()); 526 } 527 if (proto.hasBackupState()) { 528 context.setState(BackupInfo.BackupState.valueOf(proto.getBackupState().name())); 529 } 530 531 context 532 .setHLogTargetDir(BackupUtils.getLogBackupDir(proto.getBackupRootDir(), proto.getBackupId())); 533 534 if (proto.hasBackupPhase()) { 535 context.setPhase(BackupPhase.valueOf(proto.getBackupPhase().name())); 536 } 537 if (proto.hasProgress()) { 538 context.setProgress(proto.getProgress()); 539 } 540 context.setStartTs(proto.getStartTs()); 541 context.setBackupRootDir(proto.getBackupRootDir()); 542 context.setType(BackupType.valueOf(proto.getBackupType().name())); 543 context.setWorkers(proto.getWorkersNumber()); 544 context.setBandwidth(proto.getBandwidth()); 545 context.setTotalBytesCopied(proto.getTotalBytesCopied()); 546 context.setNoChecksumVerify(proto.getNoChecksumVerify()); 547 if (proto.getIncrBackupFileListCount() > 0) { 548 context.setIncrBackupFileList(new ArrayList<>(proto.getIncrBackupFileListList())); 549 } 550 if (proto.getIncrTimestampMapCount() > 0) { 551 context.setIncrTimestampMap(getTableSetTimestampMap(proto.getIncrTimestampMapMap())); 552 } 553 return context; 554 } 555 556 private static Map<TableName, BackupTableInfo> toMap(List<BackupProtos.BackupTableInfo> list) { 557 HashMap<TableName, BackupTableInfo> map = new HashMap<>(); 558 for (BackupProtos.BackupTableInfo tbs : list) { 559 map.put(ProtobufUtil.toTableName(tbs.getTableName()), BackupTableInfo.convert(tbs)); 560 } 561 return map; 562 } 563 564 private static Map<TableName, Map<String, Long>> 565 getTableSetTimestampMap(Map<String, BackupProtos.BackupInfo.RSTimestampMap> map) { 566 Map<TableName, Map<String, Long>> tableSetTimestampMap = new HashMap<>(); 567 for (Entry<String, BackupProtos.BackupInfo.RSTimestampMap> entry : map.entrySet()) { 568 tableSetTimestampMap.put(TableName.valueOf(entry.getKey()), 569 entry.getValue().getRsTimestampMap()); 570 } 571 572 return tableSetTimestampMap; 573 } 574 575 public String getShortDescription() { 576 StringBuilder sb = new StringBuilder(); 577 sb.append("{"); 578 sb.append("ID=" + backupId).append(","); 579 sb.append("Type=" + getType()).append(","); 580 sb.append("Tables=" + getTableListAsString()).append(","); 581 sb.append("State=" + getState()).append(","); 582 Calendar cal = Calendar.getInstance(); 583 cal.setTimeInMillis(getStartTs()); 584 Date date = cal.getTime(); 585 sb.append("Start time=" + date).append(","); 586 if (state == BackupState.FAILED) { 587 sb.append("Failed message=" + getFailedMsg()).append(","); 588 } else if (state == BackupState.RUNNING) { 589 sb.append("Phase=" + getPhase()).append(","); 590 } else if (state == BackupState.COMPLETE) { 591 cal = Calendar.getInstance(); 592 cal.setTimeInMillis(getCompleteTs()); 593 date = cal.getTime(); 594 sb.append("End time=" + date).append(","); 595 } 596 sb.append("Progress=" + getProgress() + "%"); 597 sb.append("}"); 598 599 return sb.toString(); 600 } 601 602 public String getStatusAndProgressAsString() { 603 StringBuilder sb = new StringBuilder(); 604 sb.append("id: ").append(getBackupId()).append(" state: ").append(getState()) 605 .append(" progress: ").append(getProgress()); 606 return sb.toString(); 607 } 608 609 public String getTableListAsString() { 610 StringBuilder sb = new StringBuilder(); 611 sb.append("{"); 612 sb.append(StringUtils.join(backupTableInfoMap.keySet(), ",")); 613 sb.append("}"); 614 return sb.toString(); 615 } 616 617 /** 618 * We use only time stamps to compare objects during sort operation 619 */ 620 @Override 621 public int compareTo(BackupInfo o) { 622 Long thisTS = 623 Long.valueOf(this.getBackupId().substring(this.getBackupId().lastIndexOf("_") + 1)); 624 Long otherTS = Long.valueOf(o.getBackupId().substring(o.getBackupId().lastIndexOf("_") + 1)); 625 return thisTS.compareTo(otherTS); 626 } 627}