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