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