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 static org.apache.hadoop.hbase.backup.BackupRestoreConstants.CONF_CONTINUOUS_BACKUP_WAL_DIR; 021import static org.apache.hadoop.hbase.backup.BackupRestoreConstants.LONG_OPTION_PITR_BACKUP_PATH; 022import static org.apache.hadoop.hbase.backup.BackupRestoreConstants.LONG_OPTION_TO_DATETIME; 023import static org.apache.hadoop.hbase.backup.BackupRestoreConstants.OPTION_PITR_BACKUP_PATH; 024import static org.apache.hadoop.hbase.backup.BackupRestoreConstants.OPTION_PITR_BACKUP_PATH_DESC; 025import static org.apache.hadoop.hbase.backup.BackupRestoreConstants.OPTION_TO_DATETIME; 026import static org.apache.hadoop.hbase.backup.BackupRestoreConstants.OPTION_TO_DATETIME_DESC; 027 028import java.net.URI; 029import org.apache.hadoop.conf.Configuration; 030import org.apache.hadoop.fs.FileSystem; 031import org.apache.hadoop.fs.Path; 032import org.apache.hadoop.hbase.HBaseConfiguration; 033import org.apache.hadoop.hbase.TableName; 034import org.apache.hadoop.hbase.backup.impl.BackupAdminImpl; 035import org.apache.hadoop.hbase.backup.util.BackupUtils; 036import org.apache.hadoop.hbase.client.Connection; 037import org.apache.hadoop.hbase.client.ConnectionFactory; 038import org.apache.hadoop.hbase.util.CommonFSUtils; 039import org.apache.hadoop.util.ToolRunner; 040import org.apache.yetus.audience.InterfaceAudience; 041 042import org.apache.hbase.thirdparty.com.google.common.base.Strings; 043 044/** 045 * Command-line entry point for restore operation 046 */ 047@InterfaceAudience.Private 048public class PointInTimeRestoreDriver extends AbstractRestoreDriver { 049 private static final String USAGE_STRING = """ 050 Usage: hbase pitr [options] 051 <backup_path> Backup Path to use for Point in Time Restore 052 table(s) Comma-separated list of tables to restore 053 """; 054 055 @Override 056 protected int executeRestore(boolean check, TableName[] fromTables, TableName[] toTables, 057 boolean isOverwrite) { 058 String walBackupDir = getConf().get(CONF_CONTINUOUS_BACKUP_WAL_DIR); 059 if (Strings.isNullOrEmpty(walBackupDir)) { 060 System.err.printf( 061 "Point-in-Time Restore requires the WAL backup directory (%s) to replay logs after full and incremental backups. " 062 + "Set this property if you need Point-in-Time Restore. Otherwise, use the normal restore process with the appropriate backup ID.%n", 063 CONF_CONTINUOUS_BACKUP_WAL_DIR); 064 return -1; 065 } 066 067 String[] remainArgs = cmd.getArgs(); 068 if (remainArgs.length != 0) { 069 printToolUsage(); 070 return -1; 071 } 072 073 String backupRootDir = cmd.getOptionValue(OPTION_PITR_BACKUP_PATH); 074 075 try (final Connection conn = ConnectionFactory.createConnection(conf); 076 BackupAdmin client = new BackupAdminImpl(conn)) { 077 // Get the replication checkpoint (last known safe point for Continuous Backup) 078 long replicationCheckpoint = BackupUtils.getReplicationCheckpoint(conn); 079 long endTime = replicationCheckpoint; 080 081 if (cmd.hasOption(OPTION_TO_DATETIME)) { 082 String time = cmd.getOptionValue(OPTION_TO_DATETIME); 083 try { 084 endTime = Long.parseLong(time); 085 // Convert seconds to milliseconds if input is in seconds 086 if (endTime < 10_000_000_000L) { 087 endTime *= 1000; 088 } 089 } catch (NumberFormatException e) { 090 System.out.println("ERROR: Invalid timestamp format for --to-datetime: " + time); 091 printToolUsage(); 092 return -5; 093 } 094 } 095 096 // Ensure the requested restore time does not exceed the replication checkpoint 097 if (endTime > replicationCheckpoint) { 098 LOG.error( 099 "ERROR: Requested restore time ({}) exceeds the last known safe replication checkpoint ({}). " 100 + "Please choose a time before this checkpoint to ensure data consistency.", 101 endTime, replicationCheckpoint); 102 return -5; 103 } 104 105 // TODO: Currently hardcoding keepOriginalSplits=false and restoreRootDir via tmp dir. 106 // These should come from user input (same issue exists in normal restore). 107 // Expose them as configurable options in future. 108 PointInTimeRestoreRequest pointInTimeRestoreRequest = 109 new PointInTimeRestoreRequest.Builder().withBackupRootDir(backupRootDir).withCheck(check) 110 .withFromTables(fromTables).withToTables(toTables).withOverwrite(isOverwrite) 111 .withToDateTime(endTime).withKeepOriginalSplits(false).withRestoreRootDir( 112 BackupUtils.getTmpRestoreOutputDir(FileSystem.get(conf), conf).toString()) 113 .build(); 114 115 client.pointInTimeRestore(pointInTimeRestoreRequest); 116 } catch (Exception e) { 117 LOG.error("Error while running restore backup", e); 118 return -5; 119 } 120 return 0; 121 } 122 123 @Override 124 protected void addOptions() { 125 super.addOptions(); 126 addOptWithArg(OPTION_TO_DATETIME, LONG_OPTION_TO_DATETIME, OPTION_TO_DATETIME_DESC); 127 addOptWithArg(OPTION_PITR_BACKUP_PATH, LONG_OPTION_PITR_BACKUP_PATH, 128 OPTION_PITR_BACKUP_PATH_DESC); 129 } 130 131 public static void main(String[] args) throws Exception { 132 Configuration conf = HBaseConfiguration.create(); 133 Path rootDir = CommonFSUtils.getRootDir(conf); 134 URI defaultFs = rootDir.getFileSystem(conf).getUri(); 135 CommonFSUtils.setFsDefault(conf, new Path(defaultFs)); 136 int ret = ToolRunner.run(conf, new PointInTimeRestoreDriver(), args); 137 System.exit(ret); 138 } 139 140 @Override 141 protected String getUsageString() { 142 return USAGE_STRING; 143 } 144}