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}