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