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