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