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