View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.snapshot;
20  
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.io.InterruptedIOException;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Set;
27  import java.util.concurrent.Callable;
28  import java.util.concurrent.ExecutorService;
29  import java.util.concurrent.ExecutionException;
30  import java.util.concurrent.ExecutorCompletionService;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.hadoop.hbase.classification.InterfaceAudience;
35  import org.apache.hadoop.conf.Configuration;
36  import org.apache.hadoop.fs.FileStatus;
37  import org.apache.hadoop.fs.FileSystem;
38  import org.apache.hadoop.fs.Path;
39  import org.apache.hadoop.hbase.HRegionInfo;
40  import org.apache.hadoop.hbase.TableName;
41  import org.apache.hadoop.hbase.io.HFileLink;
42  import org.apache.hadoop.hbase.mob.MobUtils;
43  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
44  import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;
45  import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
46  import org.apache.hadoop.hbase.util.HFileArchiveUtil;
47  
48  /**
49   * Utility methods for interacting with the snapshot referenced files.
50   */
51  @InterfaceAudience.Private
52  public final class SnapshotReferenceUtil {
53    private static final Log LOG = LogFactory.getLog(SnapshotReferenceUtil.class);
54  
55    public interface StoreFileVisitor {
56      void storeFile(final HRegionInfo regionInfo, final String familyName,
57         final SnapshotRegionManifest.StoreFile storeFile) throws IOException;
58    }
59  
60    public interface SnapshotVisitor extends StoreFileVisitor {
61    }
62  
63    private SnapshotReferenceUtil() {
64      // private constructor for utility class
65    }
66  
67    /**
68     * Iterate over the snapshot store files
69     *
70     * @param conf The current {@link Configuration} instance.
71     * @param fs {@link FileSystem}
72     * @param snapshotDir {@link Path} to the Snapshot directory
73     * @param visitor callback object to get the referenced files
74     * @throws IOException if an error occurred while scanning the directory
75     */
76    public static void visitReferencedFiles(final Configuration conf, final FileSystem fs,
77        final Path snapshotDir, final SnapshotVisitor visitor)
78        throws IOException {
79      SnapshotDescription desc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);
80      visitReferencedFiles(conf, fs, snapshotDir, desc, visitor);
81    }
82  
83    /**
84     * Iterate over the snapshot store files, restored.edits and logs
85     *
86     * @param conf The current {@link Configuration} instance.
87     * @param fs {@link FileSystem}
88     * @param snapshotDir {@link Path} to the Snapshot directory
89     * @param desc the {@link SnapshotDescription} of the snapshot to verify
90     * @param visitor callback object to get the referenced files
91     * @throws IOException if an error occurred while scanning the directory
92     */
93    public static void visitReferencedFiles(final Configuration conf, final FileSystem fs,
94        final Path snapshotDir, final SnapshotDescription desc, final SnapshotVisitor visitor)
95        throws IOException {
96      visitTableStoreFiles(conf, fs, snapshotDir, desc, visitor);
97    }
98  
99    /**©
100    * Iterate over the snapshot store files
101    *
102    * @param conf The current {@link Configuration} instance.
103    * @param fs {@link FileSystem}
104    * @param snapshotDir {@link Path} to the Snapshot directory
105    * @param desc the {@link SnapshotDescription} of the snapshot to verify
106    * @param visitor callback object to get the store files
107    * @throws IOException if an error occurred while scanning the directory
108    */
109   static void visitTableStoreFiles(final Configuration conf, final FileSystem fs,
110       final Path snapshotDir, final SnapshotDescription desc, final StoreFileVisitor visitor)
111       throws IOException {
112     SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, desc);
113     List<SnapshotRegionManifest> regionManifests = manifest.getRegionManifests();
114     if (regionManifests == null || regionManifests.size() == 0) {
115       LOG.debug("No manifest files present: " + snapshotDir);
116       return;
117     }
118 
119     for (SnapshotRegionManifest regionManifest: regionManifests) {
120       visitRegionStoreFiles(regionManifest, visitor);
121     }
122   }
123 
124   /**
125    * Iterate over the snapshot store files in the specified region
126    *
127    * @param manifest snapshot manifest to inspect
128    * @param visitor callback object to get the store files
129    * @throws IOException if an error occurred while scanning the directory
130    */
131   static void visitRegionStoreFiles(final SnapshotRegionManifest manifest,
132       final StoreFileVisitor visitor) throws IOException {
133     HRegionInfo regionInfo = HRegionInfo.convert(manifest.getRegionInfo());
134     for (SnapshotRegionManifest.FamilyFiles familyFiles: manifest.getFamilyFilesList()) {
135       String familyName = familyFiles.getFamilyName().toStringUtf8();
136       for (SnapshotRegionManifest.StoreFile storeFile: familyFiles.getStoreFilesList()) {
137         visitor.storeFile(regionInfo, familyName, storeFile);
138       }
139     }
140   }
141 
142   /**
143    * Verify the validity of the snapshot
144    *
145    * @param conf The current {@link Configuration} instance.
146    * @param fs {@link FileSystem}
147    * @param snapshotDir {@link Path} to the Snapshot directory of the snapshot to verify
148    * @param snapshotDesc the {@link SnapshotDescription} of the snapshot to verify
149    * @throws CorruptedSnapshotException if the snapshot is corrupted
150    * @throws IOException if an error occurred while scanning the directory
151    */
152   public static void verifySnapshot(final Configuration conf, final FileSystem fs,
153       final Path snapshotDir, final SnapshotDescription snapshotDesc) throws IOException {
154     SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshotDesc);
155     verifySnapshot(conf, fs, manifest);
156   }
157 
158   /**
159    * Verify the validity of the snapshot
160    *
161    * @param conf The current {@link Configuration} instance.
162    * @param fs {@link FileSystem}
163    * @param manifest snapshot manifest to inspect
164    * @throws CorruptedSnapshotException if the snapshot is corrupted
165    * @throws IOException if an error occurred while scanning the directory
166    */
167   public static void verifySnapshot(final Configuration conf, final FileSystem fs,
168       final SnapshotManifest manifest) throws IOException {
169     final SnapshotDescription snapshotDesc = manifest.getSnapshotDescription();
170     final Path snapshotDir = manifest.getSnapshotDir();
171     concurrentVisitReferencedFiles(conf, fs, manifest, new StoreFileVisitor() {
172       @Override
173       public void storeFile(final HRegionInfo regionInfo, final String family,
174           final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
175         verifyStoreFile(conf, fs, snapshotDir, snapshotDesc, regionInfo, family, storeFile);
176       }
177     });
178   }
179 
180   public static void concurrentVisitReferencedFiles(final Configuration conf, final FileSystem fs,
181       final SnapshotManifest manifest, final StoreFileVisitor visitor) throws IOException {
182     final SnapshotDescription snapshotDesc = manifest.getSnapshotDescription();
183     final Path snapshotDir = manifest.getSnapshotDir();
184 
185     List<SnapshotRegionManifest> regionManifests = manifest.getRegionManifests();
186     if (regionManifests == null || regionManifests.size() == 0) {
187       LOG.debug("No manifest files present: " + snapshotDir);
188       return;
189     }
190 
191     ExecutorService exec = SnapshotManifest.createExecutor(conf, "VerifySnapshot");
192     final ExecutorCompletionService<Void> completionService =
193       new ExecutorCompletionService<Void>(exec);
194     try {
195       for (final SnapshotRegionManifest regionManifest: regionManifests) {
196         completionService.submit(new Callable<Void>() {
197           @Override
198           public Void call() throws IOException {
199             visitRegionStoreFiles(regionManifest, visitor);
200             return null;
201           }
202         });
203       }
204       try {
205         for (int i = 0; i < regionManifests.size(); ++i) {
206           completionService.take().get();
207         }
208       } catch (InterruptedException e) {
209         throw new InterruptedIOException(e.getMessage());
210       } catch (ExecutionException e) {
211         if (e.getCause() instanceof CorruptedSnapshotException) {
212           throw new CorruptedSnapshotException(e.getCause().getMessage(), snapshotDesc);
213         } else {
214           IOException ex = new IOException();
215           ex.initCause(e.getCause());
216           throw ex;
217         }
218       }
219     } finally {
220       exec.shutdown();
221     }
222   }
223 
224   /**
225    * Verify the validity of the snapshot store file
226    *
227    * @param conf The current {@link Configuration} instance.
228    * @param fs {@link FileSystem}
229    * @param snapshotDir {@link Path} to the Snapshot directory of the snapshot to verify
230    * @param snapshot the {@link SnapshotDescription} of the snapshot to verify
231    * @param regionInfo {@link HRegionInfo} of the region that contains the store file
232    * @param family family that contains the store file
233    * @param storeFile the store file to verify
234    * @throws CorruptedSnapshotException if the snapshot is corrupted
235    * @throws IOException if an error occurred while scanning the directory
236    */
237   private static void verifyStoreFile(final Configuration conf, final FileSystem fs,
238       final Path snapshotDir, final SnapshotDescription snapshot, final HRegionInfo regionInfo,
239       final String family, final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
240     TableName table = TableName.valueOf(snapshot.getTable());
241     String fileName = storeFile.getName();
242 
243     Path refPath = null;
244     if (StoreFileInfo.isReference(fileName)) {
245       // If is a reference file check if the parent file is present in the snapshot
246       refPath = new Path(new Path(regionInfo.getEncodedName(), family), fileName);
247       refPath = StoreFileInfo.getReferredToFile(refPath);
248       String refRegion = refPath.getParent().getParent().getName();
249       refPath = HFileLink.createPath(table, refRegion, family, refPath.getName());
250       if (!HFileLink.buildFromHFileLinkPattern(conf, refPath).exists(fs)) {
251         throw new CorruptedSnapshotException("Missing parent hfile for: " + fileName +
252           " path=" + refPath, snapshot);
253       }
254 
255       if (storeFile.hasReference()) {
256         // We don't really need to look for the file on-disk
257         // we already have the Reference information embedded here.
258         return;
259       }
260     }
261 
262     Path linkPath;
263     if (refPath != null && HFileLink.isHFileLink(refPath)) {
264       linkPath = new Path(family, refPath.getName());
265     } else if (HFileLink.isHFileLink(fileName)) {
266       linkPath = new Path(family, fileName);
267     } else {
268       linkPath = new Path(family, HFileLink.createHFileLinkName(
269               table, regionInfo.getEncodedName(), fileName));
270     }
271 
272     // check if the linked file exists (in the archive, or in the table dir)
273     HFileLink link = null;
274     if (MobUtils.isMobRegionInfo(regionInfo)) {
275       // for mob region
276       link = HFileLink.buildFromHFileLinkPattern(MobUtils.getQualifiedMobRootDir(conf),
277           HFileArchiveUtil.getArchivePath(conf), linkPath);
278     } else {
279       // not mob region
280       link = HFileLink.buildFromHFileLinkPattern(conf, linkPath);
281     }
282     try {
283       FileStatus fstat = link.getFileStatus(fs);
284       if (storeFile.hasFileSize() && storeFile.getFileSize() != fstat.getLen()) {
285         String msg = "hfile: " + fileName + " size does not match with the expected one. " +
286           " found=" + fstat.getLen() + " expected=" + storeFile.getFileSize();
287         LOG.error(msg);
288         throw new CorruptedSnapshotException(msg, snapshot);
289       }
290     } catch (FileNotFoundException e) {
291       String msg = "Can't find hfile: " + fileName + " in the real (" +
292           link.getOriginPath() + ") or archive (" + link.getArchivePath()
293           + ") directory for the primary table.";
294       LOG.error(msg);
295       throw new CorruptedSnapshotException(msg, snapshot);
296     }
297   }
298 
299   /**
300    * Returns the store file names in the snapshot.
301    *
302    * @param conf The current {@link Configuration} instance.
303    * @param fs {@link FileSystem}
304    * @param snapshotDir {@link Path} to the Snapshot directory
305    * @throws IOException if an error occurred while scanning the directory
306    * @return the names of hfiles in the specified snaphot
307    */
308   public static Set<String> getHFileNames(final Configuration conf, final FileSystem fs,
309       final Path snapshotDir) throws IOException {
310     SnapshotDescription desc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);
311     return getHFileNames(conf, fs, snapshotDir, desc);
312   }
313 
314   /**
315    * Returns the store file names in the snapshot.
316    *
317    * @param conf The current {@link Configuration} instance.
318    * @param fs {@link FileSystem}
319    * @param snapshotDir {@link Path} to the Snapshot directory
320    * @param snapshotDesc the {@link SnapshotDescription} of the snapshot to inspect
321    * @throws IOException if an error occurred while scanning the directory
322    * @return the names of hfiles in the specified snaphot
323    */
324   private static Set<String> getHFileNames(final Configuration conf, final FileSystem fs,
325       final Path snapshotDir, final SnapshotDescription snapshotDesc)
326       throws IOException {
327     final Set<String> names = new HashSet<String>();
328     visitTableStoreFiles(conf, fs, snapshotDir, snapshotDesc, new StoreFileVisitor() {
329       @Override
330       public void storeFile(final HRegionInfo regionInfo, final String family,
331             final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
332         String hfile = storeFile.getName();
333         if (HFileLink.isHFileLink(hfile)) {
334           names.add(HFileLink.getReferencedHFileName(hfile));
335         } else {
336           names.add(hfile);
337         }
338       }
339     });
340     return names;
341   }
342 }