001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.hbase.snapshot; 020 021import java.io.FileNotFoundException; 022import java.io.IOException; 023import java.io.InterruptedIOException; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Set; 027import java.util.concurrent.Callable; 028import java.util.concurrent.ExecutionException; 029import java.util.concurrent.ExecutorCompletionService; 030import java.util.concurrent.ExecutorService; 031 032import org.apache.hadoop.conf.Configuration; 033import org.apache.hadoop.fs.FileStatus; 034import org.apache.hadoop.fs.FileSystem; 035import org.apache.hadoop.fs.Path; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.client.RegionInfo; 038import org.apache.hadoop.hbase.io.HFileLink; 039import org.apache.hadoop.hbase.mob.MobUtils; 040import org.apache.hadoop.hbase.regionserver.StoreFileInfo; 041import org.apache.hadoop.hbase.util.HFileArchiveUtil; 042import org.apache.yetus.audience.InterfaceAudience; 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 046import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotDescription; 047import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotRegionManifest; 048 049/** 050 * Utility methods for interacting with the snapshot referenced files. 051 */ 052@InterfaceAudience.Private 053public final class SnapshotReferenceUtil { 054 private static final Logger LOG = LoggerFactory.getLogger(SnapshotReferenceUtil.class); 055 056 public interface StoreFileVisitor { 057 void storeFile(final RegionInfo regionInfo, final String familyName, 058 final SnapshotRegionManifest.StoreFile storeFile) throws IOException; 059 } 060 061 public interface SnapshotVisitor extends StoreFileVisitor { 062 } 063 064 private SnapshotReferenceUtil() { 065 // private constructor for utility class 066 } 067 068 /** 069 * Iterate over the snapshot store files 070 * 071 * @param conf The current {@link Configuration} instance. 072 * @param fs {@link FileSystem} 073 * @param snapshotDir {@link Path} to the Snapshot directory 074 * @param visitor callback object to get the referenced files 075 * @throws IOException if an error occurred while scanning the directory 076 */ 077 public static void visitReferencedFiles(final Configuration conf, final FileSystem fs, 078 final Path snapshotDir, final SnapshotVisitor visitor) 079 throws IOException { 080 SnapshotDescription desc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir); 081 visitReferencedFiles(conf, fs, snapshotDir, desc, visitor); 082 } 083 084 /** 085 * Iterate over the snapshot store files, restored.edits and logs 086 * 087 * @param conf The current {@link Configuration} instance. 088 * @param fs {@link FileSystem} 089 * @param snapshotDir {@link Path} to the Snapshot directory 090 * @param desc the {@link SnapshotDescription} of the snapshot to verify 091 * @param visitor callback object to get the referenced files 092 * @throws IOException if an error occurred while scanning the directory 093 */ 094 public static void visitReferencedFiles(final Configuration conf, final FileSystem fs, 095 final Path snapshotDir, final SnapshotDescription desc, final SnapshotVisitor visitor) 096 throws IOException { 097 visitTableStoreFiles(conf, fs, snapshotDir, desc, visitor); 098 } 099 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.isEmpty()) { 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 RegionInfo regionInfo = ProtobufUtil.toRegionInfo(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 RegionInfo 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.isEmpty()) { 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.isEmpty()) { 209 LOG.debug("No manifest files present: " + snapshotDir); 210 return; 211 } 212 213 final ExecutorCompletionService<Void> completionService = new ExecutorCompletionService<>(exec); 214 215 for (final SnapshotRegionManifest regionManifest : regionManifests) { 216 completionService.submit(new Callable<Void>() { 217 @Override public Void call() throws IOException { 218 visitRegionStoreFiles(regionManifest, visitor); 219 return null; 220 } 221 }); 222 } 223 try { 224 for (int i = 0; i < regionManifests.size(); ++i) { 225 completionService.take().get(); 226 } 227 } catch (InterruptedException e) { 228 throw new InterruptedIOException(e.getMessage()); 229 } catch (ExecutionException e) { 230 if (e.getCause() instanceof CorruptedSnapshotException) { 231 throw new CorruptedSnapshotException(e.getCause().getMessage(), 232 ProtobufUtil.createSnapshotDesc(snapshotDesc)); 233 } else { 234 IOException ex = new IOException(); 235 ex.initCause(e.getCause()); 236 throw ex; 237 } 238 } 239 } 240 241 /** 242 * Verify the validity of the snapshot store file 243 * 244 * @param conf The current {@link Configuration} instance. 245 * @param fs {@link FileSystem} 246 * @param snapshotDir {@link Path} to the Snapshot directory of the snapshot to verify 247 * @param snapshot the {@link SnapshotDescription} of the snapshot to verify 248 * @param regionInfo {@link RegionInfo} of the region that contains the store file 249 * @param family family that contains the store file 250 * @param storeFile the store file to verify 251 * @throws CorruptedSnapshotException if the snapshot is corrupted 252 * @throws IOException if an error occurred while scanning the directory 253 */ 254 private static void verifyStoreFile(final Configuration conf, final FileSystem fs, 255 final Path snapshotDir, final SnapshotDescription snapshot, final RegionInfo regionInfo, 256 final String family, final SnapshotRegionManifest.StoreFile storeFile) throws IOException { 257 TableName table = TableName.valueOf(snapshot.getTable()); 258 String fileName = storeFile.getName(); 259 260 Path refPath = null; 261 if (StoreFileInfo.isReference(fileName)) { 262 // If is a reference file check if the parent file is present in the snapshot 263 refPath = new Path(new Path(regionInfo.getEncodedName(), family), fileName); 264 refPath = StoreFileInfo.getReferredToFile(refPath); 265 String refRegion = refPath.getParent().getParent().getName(); 266 refPath = HFileLink.createPath(table, refRegion, family, refPath.getName()); 267 if (!HFileLink.buildFromHFileLinkPattern(conf, refPath).exists(fs)) { 268 throw new CorruptedSnapshotException( 269 "Missing parent hfile for: " + fileName + " path=" + refPath, 270 ProtobufUtil.createSnapshotDesc(snapshot)); 271 } 272 273 if (storeFile.hasReference()) { 274 // We don't really need to look for the file on-disk 275 // we already have the Reference information embedded here. 276 return; 277 } 278 } 279 280 Path linkPath; 281 if (refPath != null && HFileLink.isHFileLink(refPath)) { 282 linkPath = new Path(family, refPath.getName()); 283 } else if (HFileLink.isHFileLink(fileName)) { 284 linkPath = new Path(family, fileName); 285 } else { 286 linkPath = new Path(family, HFileLink.createHFileLinkName( 287 table, regionInfo.getEncodedName(), fileName)); 288 } 289 290 // check if the linked file exists (in the archive, or in the table dir) 291 HFileLink link = null; 292 if (MobUtils.isMobRegionInfo(regionInfo)) { 293 // for mob region 294 link = HFileLink.buildFromHFileLinkPattern(MobUtils.getQualifiedMobRootDir(conf), 295 HFileArchiveUtil.getArchivePath(conf), linkPath); 296 } else { 297 // not mob region 298 link = HFileLink.buildFromHFileLinkPattern(conf, linkPath); 299 } 300 try { 301 FileStatus fstat = link.getFileStatus(fs); 302 if (storeFile.hasFileSize() && storeFile.getFileSize() != fstat.getLen()) { 303 String msg = "hfile: " + fileName + " size does not match with the expected one. " + 304 " found=" + fstat.getLen() + " expected=" + storeFile.getFileSize(); 305 LOG.error(msg); 306 throw new CorruptedSnapshotException(msg, 307 ProtobufUtil.createSnapshotDesc(snapshot)); 308 } 309 } catch (FileNotFoundException e) { 310 String msg = "Can't find hfile: " + fileName + " in the real (" + 311 link.getOriginPath() + ") or archive (" + link.getArchivePath() 312 + ") directory for the primary table."; 313 LOG.error(msg); 314 throw new CorruptedSnapshotException(msg, 315 ProtobufUtil.createSnapshotDesc(snapshot)); 316 } 317 } 318 319 /** 320 * Returns the store file names in the snapshot. 321 * 322 * @param conf The current {@link Configuration} instance. 323 * @param fs {@link FileSystem} 324 * @param snapshotDir {@link Path} to the Snapshot directory 325 * @throws IOException if an error occurred while scanning the directory 326 * @return the names of hfiles in the specified snaphot 327 */ 328 public static Set<String> getHFileNames(final Configuration conf, final FileSystem fs, 329 final Path snapshotDir) throws IOException { 330 SnapshotDescription desc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir); 331 return getHFileNames(conf, fs, snapshotDir, desc); 332 } 333 334 /** 335 * Returns the store file names in the snapshot. 336 * 337 * @param conf The current {@link Configuration} instance. 338 * @param fs {@link FileSystem} 339 * @param snapshotDir {@link Path} to the Snapshot directory 340 * @param snapshotDesc the {@link SnapshotDescription} of the snapshot to inspect 341 * @throws IOException if an error occurred while scanning the directory 342 * @return the names of hfiles in the specified snaphot 343 */ 344 private static Set<String> getHFileNames(final Configuration conf, final FileSystem fs, 345 final Path snapshotDir, final SnapshotDescription snapshotDesc) 346 throws IOException { 347 final Set<String> names = new HashSet<>(); 348 visitTableStoreFiles(conf, fs, snapshotDir, snapshotDesc, new StoreFileVisitor() { 349 @Override 350 public void storeFile(final RegionInfo regionInfo, final String family, 351 final SnapshotRegionManifest.StoreFile storeFile) throws IOException { 352 String hfile = storeFile.getName(); 353 if (HFileLink.isHFileLink(hfile)) { 354 names.add(HFileLink.getReferencedHFileName(hfile)); 355 } else { 356 names.add(hfile); 357 } 358 } 359 }); 360 return names; 361 } 362}