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