1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.snapshot;
20
21 import java.io.FileNotFoundException;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.concurrent.ThreadPoolExecutor;
29 import java.util.concurrent.TimeUnit;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.hadoop.hbase.classification.InterfaceAudience;
34 import org.apache.hadoop.conf.Configuration;
35 import org.apache.hadoop.fs.FSDataInputStream;
36 import org.apache.hadoop.fs.FSDataOutputStream;
37 import org.apache.hadoop.fs.FileStatus;
38 import org.apache.hadoop.fs.FileSystem;
39 import org.apache.hadoop.fs.Path;
40 import org.apache.hadoop.hbase.HColumnDescriptor;
41 import org.apache.hadoop.hbase.HRegionInfo;
42 import org.apache.hadoop.hbase.HTableDescriptor;
43 import org.apache.hadoop.hbase.TableDescriptor;
44 import org.apache.hadoop.hbase.errorhandling.ForeignExceptionSnare;
45 import org.apache.hadoop.hbase.mob.MobUtils;
46 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
47 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
48 import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos.SnapshotDataManifest;
49 import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;
50 import org.apache.hadoop.hbase.regionserver.HRegion;
51 import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
52 import org.apache.hadoop.hbase.regionserver.Store;
53 import org.apache.hadoop.hbase.regionserver.StoreFile;
54 import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
55 import org.apache.hadoop.hbase.util.Bytes;
56 import org.apache.hadoop.hbase.util.FSTableDescriptors;
57 import org.apache.hadoop.hbase.util.FSUtils;
58 import org.apache.hadoop.hbase.util.Threads;
59
60
61
62
63
64
65
66
67 @InterfaceAudience.Private
68 public final class SnapshotManifest {
69 private static final Log LOG = LogFactory.getLog(SnapshotManifest.class);
70
71 private static final String DATA_MANIFEST_NAME = "data.manifest";
72
73 private List<SnapshotRegionManifest> regionManifests;
74 private SnapshotDescription desc;
75 private HTableDescriptor htd;
76
77 private final ForeignExceptionSnare monitor;
78 private final Configuration conf;
79 private final Path workingDir;
80 private final FileSystem fs;
81
82 private SnapshotManifest(final Configuration conf, final FileSystem fs,
83 final Path workingDir, final SnapshotDescription desc,
84 final ForeignExceptionSnare monitor) {
85 this.monitor = monitor;
86 this.desc = desc;
87 this.workingDir = workingDir;
88 this.conf = conf;
89 this.fs = fs;
90 }
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105 public static SnapshotManifest create(final Configuration conf, final FileSystem fs,
106 final Path workingDir, final SnapshotDescription desc,
107 final ForeignExceptionSnare monitor) {
108 return new SnapshotManifest(conf, fs, workingDir, desc, monitor);
109 }
110
111
112
113
114
115
116
117
118
119
120 public static SnapshotManifest open(final Configuration conf, final FileSystem fs,
121 final Path workingDir, final SnapshotDescription desc) throws IOException {
122 SnapshotManifest manifest = new SnapshotManifest(conf, fs, workingDir, desc, null);
123 manifest.load();
124 return manifest;
125 }
126
127
128
129
130
131 public void addTableDescriptor(final HTableDescriptor htd) throws IOException {
132 this.htd = htd;
133 }
134
135 interface RegionVisitor<TRegion, TFamily> {
136 TRegion regionOpen(final HRegionInfo regionInfo) throws IOException;
137 void regionClose(final TRegion region) throws IOException;
138
139 TFamily familyOpen(final TRegion region, final byte[] familyName) throws IOException;
140 void familyClose(final TRegion region, final TFamily family) throws IOException;
141
142 void storeFile(final TRegion region, final TFamily family, final StoreFileInfo storeFile)
143 throws IOException;
144 }
145
146 private RegionVisitor createRegionVisitor(final SnapshotDescription desc) throws IOException {
147 switch (getSnapshotFormat(desc)) {
148 case SnapshotManifestV1.DESCRIPTOR_VERSION:
149 return new SnapshotManifestV1.ManifestBuilder(conf, fs, workingDir);
150 case SnapshotManifestV2.DESCRIPTOR_VERSION:
151 return new SnapshotManifestV2.ManifestBuilder(conf, fs, workingDir);
152 default:
153 throw new CorruptedSnapshotException("Invalid Snapshot version: "+ desc.getVersion(), desc);
154 }
155 }
156
157 public void addMobRegion(HRegionInfo regionInfo, HColumnDescriptor[] hcds) throws IOException {
158
159 RegionVisitor visitor = createRegionVisitor(desc);
160
161
162 LOG.debug("Storing mob region '" + regionInfo + "' region-info for snapshot.");
163 Object regionData = visitor.regionOpen(regionInfo);
164 monitor.rethrowException();
165
166
167 LOG.debug("Creating references for mob files");
168
169 Path mobRegionPath = MobUtils.getMobRegionPath(conf, regionInfo.getTable());
170 for (HColumnDescriptor hcd : hcds) {
171
172 if (!hcd.isMobEnabled()) {
173 continue;
174 }
175 Object familyData = visitor.familyOpen(regionData, hcd.getName());
176 monitor.rethrowException();
177
178 Path storePath = MobUtils.getMobFamilyPath(mobRegionPath, hcd.getNameAsString());
179 List<StoreFileInfo> storeFiles = getStoreFiles(storePath);
180 if (storeFiles == null) {
181 if (LOG.isDebugEnabled()) {
182 LOG.debug("No mob files under family: " + hcd.getNameAsString());
183 }
184 continue;
185 }
186
187 addReferenceFiles(visitor, regionData, familyData, storeFiles, true);
188
189 visitor.familyClose(regionData, familyData);
190 }
191 visitor.regionClose(regionData);
192 }
193
194
195
196
197
198 public void addRegion(final HRegion region) throws IOException {
199
200 RegionVisitor visitor = createRegionVisitor(desc);
201
202
203 LOG.debug("Storing '" + region + "' region-info for snapshot.");
204 Object regionData = visitor.regionOpen(region.getRegionInfo());
205 monitor.rethrowException();
206
207
208 LOG.debug("Creating references for hfiles");
209
210 for (Store store : region.getStores()) {
211
212 Object familyData = visitor.familyOpen(regionData, store.getFamily().getName());
213 monitor.rethrowException();
214
215 List<StoreFile> storeFiles = new ArrayList<StoreFile>(store.getStorefiles());
216 if (LOG.isDebugEnabled()) {
217 LOG.debug("Adding snapshot references for " + storeFiles + " hfiles");
218 }
219
220
221 for (int i = 0, sz = storeFiles.size(); i < sz; i++) {
222 StoreFile storeFile = storeFiles.get(i);
223 monitor.rethrowException();
224
225
226 LOG.debug("Adding reference for file (" + (i+1) + "/" + sz + "): " + storeFile.getPath());
227 visitor.storeFile(regionData, familyData, storeFile.getFileInfo());
228 }
229 visitor.familyClose(regionData, familyData);
230 }
231 visitor.regionClose(regionData);
232 }
233
234
235
236
237
238 public void addRegion(final Path tableDir, final HRegionInfo regionInfo) throws IOException {
239
240 RegionVisitor visitor = createRegionVisitor(desc);
241
242 boolean isMobRegion = MobUtils.isMobRegionInfo(regionInfo);
243 try {
244
245 HRegionFileSystem regionFs = HRegionFileSystem.openRegionFromFileSystem(conf, fs,
246 tableDir, regionInfo, true);
247 monitor.rethrowException();
248
249
250 LOG.debug("Storing region-info for snapshot.");
251 Object regionData = visitor.regionOpen(regionInfo);
252 monitor.rethrowException();
253
254
255 LOG.debug("Creating references for hfiles");
256
257
258
259
260
261
262 Collection<String> familyNames = regionFs.getFamilies();
263 if (familyNames != null) {
264 for (String familyName: familyNames) {
265 Object familyData = visitor.familyOpen(regionData, Bytes.toBytes(familyName));
266 monitor.rethrowException();
267
268 Collection<StoreFileInfo> storeFiles = null;
269 if (isMobRegion) {
270 Path regionPath = MobUtils.getMobRegionPath(conf, regionInfo.getTable());
271 Path storePath = MobUtils.getMobFamilyPath(regionPath, familyName);
272 storeFiles = getStoreFiles(storePath);
273 } else {
274 storeFiles = regionFs.getStoreFiles(familyName);
275 }
276
277 if (storeFiles == null) {
278 if (LOG.isDebugEnabled()) {
279 LOG.debug("No files under family: " + familyName);
280 }
281 continue;
282 }
283
284
285
286 addReferenceFiles(visitor, regionData, familyData, storeFiles, false);
287
288 visitor.familyClose(regionData, familyData);
289 }
290 }
291 visitor.regionClose(regionData);
292 } catch (IOException e) {
293
294 if (!isMobRegion) {
295 throw e;
296 }
297 }
298 }
299
300 private List<StoreFileInfo> getStoreFiles(Path storeDir) throws IOException {
301 FileStatus[] stats = FSUtils.listStatus(fs, storeDir);
302 if (stats == null) return null;
303
304 ArrayList<StoreFileInfo> storeFiles = new ArrayList<StoreFileInfo>(stats.length);
305 for (int i = 0; i < stats.length; ++i) {
306 storeFiles.add(new StoreFileInfo(conf, fs, stats[i]));
307 }
308 return storeFiles;
309 }
310
311 private void addReferenceFiles(RegionVisitor visitor, Object regionData, Object familyData,
312 Collection<StoreFileInfo> storeFiles, boolean isMob) throws IOException {
313 final String fileType = isMob ? "mob file" : "hfile";
314
315 if (LOG.isDebugEnabled()) {
316 LOG.debug(String.format("Adding snapshot references for %s %ss", storeFiles, fileType));
317 }
318
319 int i = 0;
320 int sz = storeFiles.size();
321 for (StoreFileInfo storeFile: storeFiles) {
322 monitor.rethrowException();
323
324 LOG.debug(String.format("Adding reference for %s (%d/%d): %s",
325 fileType, ++i, sz, storeFile.getPath()));
326
327
328 visitor.storeFile(regionData, familyData, storeFile);
329 }
330 }
331
332
333
334
335
336
337
338
339 private void load() throws IOException {
340 switch (getSnapshotFormat(desc)) {
341 case SnapshotManifestV1.DESCRIPTOR_VERSION: {
342 this.htd = FSTableDescriptors.getTableDescriptorFromFs(fs, workingDir)
343 .getHTableDescriptor();
344 ThreadPoolExecutor tpool = createExecutor("SnapshotManifestLoader");
345 try {
346 this.regionManifests =
347 SnapshotManifestV1.loadRegionManifests(conf, tpool, fs, workingDir, desc);
348 } finally {
349 tpool.shutdown();
350 }
351 break;
352 }
353 case SnapshotManifestV2.DESCRIPTOR_VERSION: {
354 SnapshotDataManifest dataManifest = readDataManifest();
355 if (dataManifest != null) {
356 htd = HTableDescriptor.convert(dataManifest.getTableSchema());
357 regionManifests = dataManifest.getRegionManifestsList();
358 } else {
359
360
361 List<SnapshotRegionManifest> v1Regions, v2Regions;
362 ThreadPoolExecutor tpool = createExecutor("SnapshotManifestLoader");
363 try {
364 v1Regions = SnapshotManifestV1.loadRegionManifests(conf, tpool, fs, workingDir, desc);
365 v2Regions = SnapshotManifestV2.loadRegionManifests(conf, tpool, fs, workingDir, desc);
366 } finally {
367 tpool.shutdown();
368 }
369 if (v1Regions != null && v2Regions != null) {
370 regionManifests =
371 new ArrayList<SnapshotRegionManifest>(v1Regions.size() + v2Regions.size());
372 regionManifests.addAll(v1Regions);
373 regionManifests.addAll(v2Regions);
374 } else if (v1Regions != null) {
375 regionManifests = v1Regions;
376 } else
377 regionManifests = v2Regions;
378 }
379 }
380 break;
381 }
382 default:
383 throw new CorruptedSnapshotException("Invalid Snapshot version: "+ desc.getVersion(), desc);
384 }
385 }
386
387
388
389
390 public Path getSnapshotDir() {
391 return this.workingDir;
392 }
393
394
395
396
397 public SnapshotDescription getSnapshotDescription() {
398 return this.desc;
399 }
400
401
402
403
404 public HTableDescriptor getTableDescriptor() {
405 return this.htd;
406 }
407
408
409
410
411 public List<SnapshotRegionManifest> getRegionManifests() {
412 return this.regionManifests;
413 }
414
415
416
417
418
419 public Map<String, SnapshotRegionManifest> getRegionManifestsMap() {
420 if (regionManifests == null || regionManifests.size() == 0) return null;
421
422 HashMap<String, SnapshotRegionManifest> regionsMap =
423 new HashMap<String, SnapshotRegionManifest>(regionManifests.size());
424 for (SnapshotRegionManifest manifest: regionManifests) {
425 String regionName = getRegionNameFromManifest(manifest);
426 regionsMap.put(regionName, manifest);
427 }
428 return regionsMap;
429 }
430
431 public void consolidate() throws IOException {
432 if (getSnapshotFormat(desc) == SnapshotManifestV1.DESCRIPTOR_VERSION) {
433 Path rootDir = FSUtils.getRootDir(conf);
434 LOG.info("Using old Snapshot Format");
435
436 new FSTableDescriptors(conf, fs, rootDir)
437 .createTableDescriptorForTableDirectory(workingDir, new TableDescriptor(
438 htd), false);
439 } else {
440 LOG.debug("Convert to Single Snapshot Manifest");
441 convertToV2SingleManifest();
442 }
443 }
444
445
446
447
448
449 private void convertToV2SingleManifest() throws IOException {
450
451 List<SnapshotRegionManifest> v1Regions, v2Regions;
452 ThreadPoolExecutor tpool = createExecutor("SnapshotManifestLoader");
453 try {
454 v1Regions = SnapshotManifestV1.loadRegionManifests(conf, tpool, fs, workingDir, desc);
455 v2Regions = SnapshotManifestV2.loadRegionManifests(conf, tpool, fs, workingDir, desc);
456 } finally {
457 tpool.shutdown();
458 }
459
460 SnapshotDataManifest.Builder dataManifestBuilder = SnapshotDataManifest.newBuilder();
461 dataManifestBuilder.setTableSchema(htd.convert());
462
463 if (v1Regions != null && v1Regions.size() > 0) {
464 dataManifestBuilder.addAllRegionManifests(v1Regions);
465 }
466 if (v2Regions != null && v2Regions.size() > 0) {
467 dataManifestBuilder.addAllRegionManifests(v2Regions);
468 }
469
470
471
472
473
474 SnapshotDataManifest dataManifest = dataManifestBuilder.build();
475 writeDataManifest(dataManifest);
476 this.regionManifests = dataManifest.getRegionManifestsList();
477
478
479
480
481
482
483 if (v1Regions != null && v1Regions.size() > 0) {
484 for (SnapshotRegionManifest regionManifest: v1Regions) {
485 SnapshotManifestV1.deleteRegionManifest(fs, workingDir, regionManifest);
486 }
487 }
488 if (v2Regions != null && v2Regions.size() > 0) {
489 for (SnapshotRegionManifest regionManifest: v2Regions) {
490 SnapshotManifestV2.deleteRegionManifest(fs, workingDir, regionManifest);
491 }
492 }
493 }
494
495
496
497
498 private void writeDataManifest(final SnapshotDataManifest manifest)
499 throws IOException {
500 FSDataOutputStream stream = fs.create(new Path(workingDir, DATA_MANIFEST_NAME));
501 try {
502 manifest.writeTo(stream);
503 } finally {
504 stream.close();
505 }
506 }
507
508
509
510
511 private SnapshotDataManifest readDataManifest() throws IOException {
512 FSDataInputStream in = null;
513 try {
514 in = fs.open(new Path(workingDir, DATA_MANIFEST_NAME));
515 return SnapshotDataManifest.parseFrom(in);
516 } catch (FileNotFoundException e) {
517 return null;
518 } finally {
519 if (in != null) in.close();
520 }
521 }
522
523 private ThreadPoolExecutor createExecutor(final String name) {
524 return createExecutor(conf, name);
525 }
526
527 public static ThreadPoolExecutor createExecutor(final Configuration conf, final String name) {
528 int maxThreads = conf.getInt("hbase.snapshot.thread.pool.max", 8);
529 return Threads.getBoundedCachedThreadPool(maxThreads, 30L, TimeUnit.SECONDS,
530 Threads.getNamedThreadFactory(name));
531 }
532
533
534
535
536 static String getRegionNameFromManifest(final SnapshotRegionManifest manifest) {
537 byte[] regionName = HRegionInfo.createRegionName(
538 ProtobufUtil.toTableName(manifest.getRegionInfo().getTableName()),
539 manifest.getRegionInfo().getStartKey().toByteArray(),
540 manifest.getRegionInfo().getRegionId(), true);
541 return HRegionInfo.encodeRegionName(regionName);
542 }
543
544
545
546
547 private static int getSnapshotFormat(final SnapshotDescription desc) {
548 return desc.hasVersion() ? desc.getVersion() : SnapshotManifestV1.DESCRIPTOR_VERSION;
549 }
550 }