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