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 return builder.build(); 439 } 440 441 @Override 442 public int hashCode() { 443 int hash = 33 * type.hashCode() + backupId != null ? backupId.hashCode() : 0; 444 if (backupRootDir != null) { 445 hash = 33 * hash + backupRootDir.hashCode(); 446 } 447 hash = 33 * hash + state.hashCode(); 448 hash = 33 * hash + phase.hashCode(); 449 hash = 33 * hash + (int) (startTs ^ (startTs >>> 32)); 450 hash = 33 * hash + (int) (completeTs ^ (completeTs >>> 32)); 451 hash = 33 * hash + (int) (totalBytesCopied ^ (totalBytesCopied >>> 32)); 452 if (hlogTargetDir != null) { 453 hash = 33 * hash + hlogTargetDir.hashCode(); 454 } 455 return hash; 456 } 457 458 @Override 459 public boolean equals(Object obj) { 460 if (obj instanceof BackupInfo) { 461 BackupInfo other = (BackupInfo) obj; 462 try { 463 return Bytes.equals(toByteArray(), other.toByteArray()); 464 } catch (IOException e) { 465 LOG.error(e.toString(), e); 466 return false; 467 } 468 } else { 469 return false; 470 } 471 } 472 473 @Override 474 public String toString() { 475 return backupId; 476 } 477 478 public byte[] toByteArray() throws IOException { 479 return toProtosBackupInfo().toByteArray(); 480 } 481 482 private void setBackupTableInfoMap(BackupProtos.BackupInfo.Builder builder) { 483 for (Entry<TableName, BackupTableInfo> entry : backupTableInfoMap.entrySet()) { 484 builder.addBackupTableInfo(entry.getValue().toProto()); 485 } 486 } 487 488 private void setTableSetTimestampMap(BackupProtos.BackupInfo.Builder builder) { 489 if (this.getTableSetTimestampMap() != null) { 490 for (Entry<TableName, Map<String, Long>> entry : this.getTableSetTimestampMap().entrySet()) { 491 builder.putTableSetTimestamp(entry.getKey().getNameAsString(), 492 BackupProtos.BackupInfo.RSTimestampMap.newBuilder().putAllRsTimestamp(entry.getValue()) 493 .build()); 494 } 495 } 496 } 497 498 public static BackupInfo fromByteArray(byte[] data) throws IOException { 499 return fromProto(BackupProtos.BackupInfo.parseFrom(data)); 500 } 501 502 public static BackupInfo fromStream(final InputStream stream) throws IOException { 503 return fromProto(BackupProtos.BackupInfo.parseDelimitedFrom(stream)); 504 } 505 506 public static BackupInfo fromProto(BackupProtos.BackupInfo proto) { 507 BackupInfo context = new BackupInfo(); 508 context.setBackupId(proto.getBackupId()); 509 context.setBackupTableInfoMap(toMap(proto.getBackupTableInfoList())); 510 context.setTableSetTimestampMap(getTableSetTimestampMap(proto.getTableSetTimestampMap())); 511 context.setCompleteTs(proto.getCompleteTs()); 512 if (proto.hasFailedMessage()) { 513 context.setFailedMsg(proto.getFailedMessage()); 514 } 515 if (proto.hasBackupState()) { 516 context.setState(BackupInfo.BackupState.valueOf(proto.getBackupState().name())); 517 } 518 519 context 520 .setHLogTargetDir(BackupUtils.getLogBackupDir(proto.getBackupRootDir(), proto.getBackupId())); 521 522 if (proto.hasBackupPhase()) { 523 context.setPhase(BackupPhase.valueOf(proto.getBackupPhase().name())); 524 } 525 if (proto.hasProgress()) { 526 context.setProgress(proto.getProgress()); 527 } 528 context.setStartTs(proto.getStartTs()); 529 context.setBackupRootDir(proto.getBackupRootDir()); 530 context.setType(BackupType.valueOf(proto.getBackupType().name())); 531 context.setWorkers(proto.getWorkersNumber()); 532 context.setBandwidth(proto.getBandwidth()); 533 return context; 534 } 535 536 private static Map<TableName, BackupTableInfo> toMap(List<BackupProtos.BackupTableInfo> list) { 537 HashMap<TableName, BackupTableInfo> map = new HashMap<>(); 538 for (BackupProtos.BackupTableInfo tbs : list) { 539 map.put(ProtobufUtil.toTableName(tbs.getTableName()), BackupTableInfo.convert(tbs)); 540 } 541 return map; 542 } 543 544 private static Map<TableName, Map<String, Long>> 545 getTableSetTimestampMap(Map<String, BackupProtos.BackupInfo.RSTimestampMap> map) { 546 Map<TableName, Map<String, Long>> tableSetTimestampMap = new HashMap<>(); 547 for (Entry<String, BackupProtos.BackupInfo.RSTimestampMap> entry : map.entrySet()) { 548 tableSetTimestampMap.put(TableName.valueOf(entry.getKey()), 549 entry.getValue().getRsTimestampMap()); 550 } 551 552 return tableSetTimestampMap; 553 } 554 555 public String getShortDescription() { 556 StringBuilder sb = new StringBuilder(); 557 sb.append("{"); 558 sb.append("ID=" + backupId).append(","); 559 sb.append("Type=" + getType()).append(","); 560 sb.append("Tables=" + getTableListAsString()).append(","); 561 sb.append("State=" + getState()).append(","); 562 Calendar cal = Calendar.getInstance(); 563 cal.setTimeInMillis(getStartTs()); 564 Date date = cal.getTime(); 565 sb.append("Start time=" + date).append(","); 566 if (state == BackupState.FAILED) { 567 sb.append("Failed message=" + getFailedMsg()).append(","); 568 } else if (state == BackupState.RUNNING) { 569 sb.append("Phase=" + getPhase()).append(","); 570 } else if (state == BackupState.COMPLETE) { 571 cal = Calendar.getInstance(); 572 cal.setTimeInMillis(getCompleteTs()); 573 date = cal.getTime(); 574 sb.append("End time=" + date).append(","); 575 } 576 sb.append("Progress=" + getProgress() + "%"); 577 sb.append("}"); 578 579 return sb.toString(); 580 } 581 582 public String getStatusAndProgressAsString() { 583 StringBuilder sb = new StringBuilder(); 584 sb.append("id: ").append(getBackupId()).append(" state: ").append(getState()) 585 .append(" progress: ").append(getProgress()); 586 return sb.toString(); 587 } 588 589 public String getTableListAsString() { 590 StringBuilder sb = new StringBuilder(); 591 sb.append("{"); 592 sb.append(StringUtils.join(backupTableInfoMap.keySet(), ",")); 593 sb.append("}"); 594 return sb.toString(); 595 } 596 597 /** 598 * We use only time stamps to compare objects during sort operation 599 */ 600 @Override 601 public int compareTo(BackupInfo o) { 602 Long thisTS = 603 Long.valueOf(this.getBackupId().substring(this.getBackupId().lastIndexOf("_") + 1)); 604 Long otherTS = Long.valueOf(o.getBackupId().substring(o.getBackupId().lastIndexOf("_") + 1)); 605 return thisTS.compareTo(otherTS); 606 } 607}