1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.backup.impl;
20
21 import java.io.IOException;
22 import java.nio.ByteBuffer;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.Set;
32 import java.util.TreeSet;
33
34 import org.apache.commons.lang.StringUtils;
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.hadoop.conf.Configuration;
38 import org.apache.hadoop.fs.Path;
39 import org.apache.hadoop.hbase.TableName;
40 import org.apache.hadoop.hbase.backup.BackupType;
41 import org.apache.hadoop.hbase.backup.HBackupFileSystem;
42 import org.apache.hadoop.hbase.backup.RestoreClient;
43 import org.apache.hadoop.hbase.backup.impl.BackupManifest.BackupImage;
44 import org.apache.hadoop.hbase.backup.mapreduce.MapReduceRestoreService;
45 import org.apache.hadoop.hbase.backup.util.BackupClientUtil;
46 import org.apache.hadoop.hbase.backup.util.RestoreServerUtil;
47 import org.apache.hadoop.hbase.classification.InterfaceAudience;
48 import org.apache.hadoop.hbase.classification.InterfaceStability;
49 import org.apache.hadoop.hbase.client.Admin;
50 import org.apache.hadoop.hbase.client.Connection;
51 import org.apache.hadoop.hbase.client.ConnectionFactory;
52 import org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles;
53 import org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles.LoadQueueItem;
54
55
56
57
58 @InterfaceAudience.Public
59 @InterfaceStability.Evolving
60 public final class RestoreClientImpl implements RestoreClient {
61
62 private static final Log LOG = LogFactory.getLog(RestoreClientImpl.class);
63 private Configuration conf;
64 private String fullBackupId;
65
66 public RestoreClientImpl() {
67 }
68
69 @Override
70 public void setConf(Configuration conf) {
71 this.conf = conf;
72 }
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87 @Override
88 public void restore(String backupRootDir,
89 String backupId, boolean check, TableName[] sTableArray,
90 TableName[] tTableArray, boolean isOverwrite) throws IOException {
91
92 HashMap<TableName, BackupManifest> backupManifestMap = new HashMap<>();
93
94 Path rootPath = new Path(backupRootDir);
95 HBackupFileSystem.checkImageManifestExist(backupManifestMap, sTableArray, conf, rootPath,
96 backupId);
97 try {
98
99 if (check) {
100 if (validate(backupManifestMap)) {
101 LOG.info("Checking backup images: ok");
102 } else {
103 String errMsg = "Some dependencies are missing for restore";
104 LOG.error(errMsg);
105 throw new IOException(errMsg);
106 }
107 }
108
109 if (tTableArray == null) {
110 tTableArray = sTableArray;
111 }
112
113 checkTargetTables(tTableArray, isOverwrite);
114
115 restoreStage(backupId, backupManifestMap, sTableArray, tTableArray, isOverwrite);
116 LOG.info("Restore for " + Arrays.asList(sTableArray) + " are successful!");
117 } catch (IOException e) {
118 LOG.error("ERROR: restore failed with error: " + e.getMessage());
119 throw e;
120 }
121
122 }
123
124 private boolean validate(HashMap<TableName, BackupManifest> backupManifestMap)
125 throws IOException {
126 boolean isValid = true;
127
128 for (Entry<TableName, BackupManifest> manifestEntry : backupManifestMap.entrySet()) {
129 TableName table = manifestEntry.getKey();
130 TreeSet<BackupImage> imageSet = new TreeSet<BackupImage>();
131
132 ArrayList<BackupImage> depList = manifestEntry.getValue().getDependentListByTable(table);
133 if (depList != null && !depList.isEmpty()) {
134 imageSet.addAll(depList);
135 }
136
137 LOG.info("Dependent image(s) from old to new:");
138 for (BackupImage image : imageSet) {
139 String imageDir =
140 HBackupFileSystem.getTableBackupDir(image.getRootDir(), image.getBackupId(), table);
141 if (!BackupClientUtil.checkPathExist(imageDir, conf)) {
142 LOG.error("ERROR: backup image does not exist: " + imageDir);
143 isValid = false;
144 break;
145 }
146
147 LOG.info("Backup image: " + image.getBackupId() + " for '" + table + "' is available");
148 }
149 }
150
151 return isValid;
152 }
153
154
155
156
157
158
159
160 private void checkTargetTables(TableName[] tTableArray, boolean isOverwrite)
161 throws IOException {
162 ArrayList<TableName> existTableList = new ArrayList<>();
163 ArrayList<TableName> disabledTableList = new ArrayList<>();
164
165
166 try(Connection conn = ConnectionFactory.createConnection(conf);
167 Admin admin = conn.getAdmin()) {
168 for (TableName tableName : tTableArray) {
169 if (admin.tableExists(tableName)) {
170 existTableList.add(tableName);
171 if (admin.isTableDisabled(tableName)) {
172 disabledTableList.add(tableName);
173 }
174 } else {
175 LOG.info("HBase table " + tableName
176 + " does not exist. It will be created during restore process");
177 }
178 }
179 }
180
181 if (existTableList.size() > 0) {
182 if (!isOverwrite) {
183 LOG.error("Existing table found in the restore target, please add \"-overwrite\" "
184 + "option in the command if you mean to restore to these existing tables");
185 LOG.info("Existing table list in restore target: " + existTableList);
186 throw new IOException("Existing table found in target while no \"-overwrite\" "
187 + "option found");
188 } else {
189 if (disabledTableList.size() > 0) {
190 LOG.error("Found offline table in the restore target, "
191 + "please enable them before restore with \"-overwrite\" option");
192 LOG.info("Offline table list in restore target: " + disabledTableList);
193 throw new IOException(
194 "Found offline table in the target when restore with \"-overwrite\" option");
195 }
196 }
197 }
198 }
199
200
201
202
203
204
205
206
207
208 private void restoreStage(String backupId, HashMap<TableName, BackupManifest> backupManifestMap,
209 TableName[] sTableArray, TableName[] tTableArray, boolean isOverwrite) throws IOException {
210 TreeSet<BackupImage> restoreImageSet = new TreeSet<BackupImage>();
211 boolean truncateIfExists = isOverwrite;
212 try {
213 Set<String> backupIdSet = new HashSet<>();
214 for (int i = 0; i < sTableArray.length; i++) {
215 TableName table = sTableArray[i];
216 BackupManifest manifest = backupManifestMap.get(table);
217
218
219 List<BackupImage> list = new ArrayList<BackupImage>();
220 list.add(manifest.getBackupImage());
221 List<BackupImage> depList = manifest.getDependentListByTable(table);
222 list.addAll(depList);
223 TreeSet<BackupImage> restoreList = new TreeSet<BackupImage>(list);
224 LOG.debug("need to clear merged Image. to be implemented in future jira");
225 restoreImages(restoreList.iterator(), table, tTableArray[i], truncateIfExists);
226 restoreImageSet.addAll(restoreList);
227
228 if (restoreImageSet != null && !restoreImageSet.isEmpty()) {
229 LOG.info("Restore includes the following image(s):");
230 for (BackupImage image : restoreImageSet) {
231 LOG.info("Backup: "
232 + image.getBackupId()
233 + " "
234 + HBackupFileSystem.getTableBackupDir(image.getRootDir(), image.getBackupId(),
235 table));
236 if (image.getType() == BackupType.INCREMENTAL) {
237 backupIdSet.add(image.getBackupId());
238 LOG.debug("adding " + image.getBackupId() + " for bulk load");
239 }
240 }
241 }
242 }
243 try (Connection conn = ConnectionFactory.createConnection(conf);
244 BackupSystemTable table = new BackupSystemTable(conn)) {
245 List<TableName> sTableList = Arrays.asList(sTableArray);
246 for (String id : backupIdSet) {
247 LOG.debug("restoring bulk load for " + id);
248 Map<byte[], List<Path>>[] mapForSrc = table.readBulkLoadedFiles(id, sTableList);
249 Map<LoadQueueItem, ByteBuffer> loaderResult;
250 conf.setBoolean(LoadIncrementalHFiles.ALWAYS_COPY_FILES, true);
251 LoadIncrementalHFiles loader = MapReduceRestoreService.createLoader(conf);
252 for (int i = 0; i < sTableList.size(); i++) {
253 if (mapForSrc[i] != null && !mapForSrc[i].isEmpty()) {
254 loaderResult = loader.run(null, mapForSrc[i], tTableArray[i]);
255 LOG.debug("bulk loading " + sTableList.get(i) + " to " + tTableArray[i]);
256 if (loaderResult.isEmpty()) {
257 String msg = "Couldn't bulk load for " + sTableList.get(i) + " to " +tTableArray[i];
258 LOG.error(msg);
259 throw new IOException(msg);
260 }
261 }
262 }
263 }
264 }
265 } catch (Exception e) {
266 LOG.error("Failed", e);
267 if (e instanceof IOException) {
268 throw (IOException)e;
269 }
270 throw new IOException(e);
271 }
272 LOG.debug("restoreStage finished");
273 }
274
275 static long getTsFromBackupId(String backupId) {
276 if (backupId == null) {
277 return 0;
278 }
279 return Long.valueOf(backupId.substring(backupId.lastIndexOf("_")+1));
280 }
281
282 static boolean withinRange(long a, long lower, long upper) {
283 if (a < lower || a > upper) {
284 return false;
285 }
286 return true;
287 }
288
289 int getIndex(TableName tbl, List<TableName> sTableList) {
290 for (int i = 0; i < sTableList.size(); i++) {
291 if (tbl.equals(sTableList.get(i))) {
292 return i;
293 }
294 }
295 return -1;
296 }
297
298
299
300
301
302
303
304
305 private void restoreImages(Iterator<BackupImage> it, TableName sTable,
306 TableName tTable, boolean truncateIfExists)
307 throws IOException {
308
309
310 BackupImage image = it.next();
311
312 String rootDir = image.getRootDir();
313 String backupId = image.getBackupId();
314 Path backupRoot = new Path(rootDir);
315
316
317 RestoreServerUtil restoreTool = new RestoreServerUtil(conf, backupRoot, backupId);
318 BackupManifest manifest = HBackupFileSystem.getManifest(sTable, conf, backupRoot, backupId);
319
320 Path tableBackupPath = HBackupFileSystem.getTableBackupPath(sTable, backupRoot, backupId);
321
322
323 boolean converted = false;
324
325 if (manifest.getType() == BackupType.FULL || converted) {
326 if (manifest.getType() == BackupType.FULL) {
327 fullBackupId = manifest.getBackupImage().getBackupId();
328 }
329 LOG.info("Restoring '" + sTable + "' to '" + tTable + "' from "
330 + (converted ? "converted" : "full") + " backup image " + tableBackupPath.toString());
331 restoreTool.fullRestoreTable(tableBackupPath, sTable, tTable,
332 converted, truncateIfExists);
333
334 } else {
335 throw new IOException("Unexpected backup type " + image.getType());
336 }
337
338
339 if (it.hasNext()) {
340 List<String> fileDirList = new ArrayList<String>();
341 while (it.hasNext()) {
342 BackupImage im = it.next();
343 String fileBackupDir = HBackupFileSystem.getTableBackupDir(im.getRootDir(),
344 im.getBackupId(), sTable)+ Path.SEPARATOR+"data";
345 fileDirList.add(fileBackupDir);
346 }
347 String logDirs = StringUtils.join(fileDirList, ",");
348 LOG.info("Restoring '" + sTable + "' to '" + tTable
349 + "' from file dirs: " + logDirs);
350 String[] sarr = new String[fileDirList.size()];
351 fileDirList.toArray(sarr);
352 Path[] paths = org.apache.hadoop.util.StringUtils.stringToPath(sarr);
353 restoreTool.incrementalRestoreTable(paths, new TableName[] { sTable },
354 new TableName[] { tTable });
355 }
356 LOG.info(sTable + " has been successfully restored to " + tTable);
357 }
358
359 }