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 throw new IOException(e.getCause()); 235 } 236 } 237 } 238 239 /** 240 * Verify the validity of the snapshot store file 241 * 242 * @param conf The current {@link Configuration} instance. 243 * @param fs {@link FileSystem} 244 * @param snapshotDir {@link Path} to the Snapshot directory of the snapshot to verify 245 * @param snapshot the {@link SnapshotDescription} of the snapshot to verify 246 * @param regionInfo {@link RegionInfo} of the region that contains the store file 247 * @param family family that contains the store file 248 * @param storeFile the store file to verify 249 * @throws CorruptedSnapshotException if the snapshot is corrupted 250 * @throws IOException if an error occurred while scanning the directory 251 */ 252 private static void verifyStoreFile(final Configuration conf, final FileSystem fs, 253 final Path snapshotDir, final SnapshotDescription snapshot, final RegionInfo regionInfo, 254 final String family, final SnapshotRegionManifest.StoreFile storeFile) throws IOException { 255 TableName table = TableName.valueOf(snapshot.getTable()); 256 String fileName = storeFile.getName(); 257 258 Path refPath = null; 259 if (StoreFileInfo.isReference(fileName)) { 260 // If is a reference file check if the parent file is present in the snapshot 261 refPath = new Path(new Path(regionInfo.getEncodedName(), family), fileName); 262 refPath = StoreFileInfo.getReferredToFile(refPath); 263 String refRegion = refPath.getParent().getParent().getName(); 264 refPath = HFileLink.createPath(table, refRegion, family, refPath.getName()); 265 if (!HFileLink.buildFromHFileLinkPattern(conf, refPath).exists(fs)) { 266 throw new CorruptedSnapshotException( 267 "Missing parent hfile for: " + fileName + " path=" + refPath, 268 ProtobufUtil.createSnapshotDesc(snapshot)); 269 } 270 271 if (storeFile.hasReference()) { 272 // We don't really need to look for the file on-disk 273 // we already have the Reference information embedded here. 274 return; 275 } 276 } 277 278 Path linkPath; 279 if (refPath != null && HFileLink.isHFileLink(refPath)) { 280 linkPath = new Path(family, refPath.getName()); 281 } else if (HFileLink.isHFileLink(fileName)) { 282 linkPath = new Path(family, fileName); 283 } else { 284 linkPath = new Path(family, HFileLink.createHFileLinkName( 285 table, regionInfo.getEncodedName(), fileName)); 286 } 287 288 // check if the linked file exists (in the archive, or in the table dir) 289 HFileLink link = null; 290 if (MobUtils.isMobRegionInfo(regionInfo)) { 291 // for mob region 292 link = HFileLink.buildFromHFileLinkPattern(MobUtils.getQualifiedMobRootDir(conf), 293 HFileArchiveUtil.getArchivePath(conf), linkPath); 294 } else { 295 // not mob region 296 link = HFileLink.buildFromHFileLinkPattern(conf, linkPath); 297 } 298 try { 299 FileStatus fstat = link.getFileStatus(fs); 300 if (storeFile.hasFileSize() && storeFile.getFileSize() != fstat.getLen()) { 301 String msg = "hfile: " + fileName + " size does not match with the expected one. " + 302 " found=" + fstat.getLen() + " expected=" + storeFile.getFileSize(); 303 LOG.error(msg); 304 throw new CorruptedSnapshotException(msg, 305 ProtobufUtil.createSnapshotDesc(snapshot)); 306 } 307 } catch (FileNotFoundException e) { 308 String msg = "Can't find hfile: " + fileName + " in the real (" + 309 link.getOriginPath() + ") or archive (" + link.getArchivePath() 310 + ") directory for the primary table."; 311 LOG.error(msg); 312 throw new CorruptedSnapshotException(msg, 313 ProtobufUtil.createSnapshotDesc(snapshot)); 314 } 315 } 316 317 /** 318 * Returns the store file names in the snapshot. 319 * 320 * @param conf The current {@link Configuration} instance. 321 * @param fs {@link FileSystem} 322 * @param snapshotDir {@link Path} to the Snapshot directory 323 * @throws IOException if an error occurred while scanning the directory 324 * @return the names of hfiles in the specified snaphot 325 */ 326 public static Set<String> getHFileNames(final Configuration conf, final FileSystem fs, 327 final Path snapshotDir) throws IOException { 328 SnapshotDescription desc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir); 329 return getHFileNames(conf, fs, snapshotDir, desc); 330 } 331 332 /** 333 * Returns the store file names in the snapshot. 334 * 335 * @param conf The current {@link Configuration} instance. 336 * @param fs {@link FileSystem} 337 * @param snapshotDir {@link Path} to the Snapshot directory 338 * @param snapshotDesc the {@link SnapshotDescription} of the snapshot to inspect 339 * @throws IOException if an error occurred while scanning the directory 340 * @return the names of hfiles in the specified snaphot 341 */ 342 private static Set<String> getHFileNames(final Configuration conf, final FileSystem fs, 343 final Path snapshotDir, final SnapshotDescription snapshotDesc) 344 throws IOException { 345 final Set<String> names = new HashSet<>(); 346 visitTableStoreFiles(conf, fs, snapshotDir, snapshotDesc, new StoreFileVisitor() { 347 @Override 348 public void storeFile(final RegionInfo regionInfo, final String family, 349 final SnapshotRegionManifest.StoreFile storeFile) throws IOException { 350 String hfile = storeFile.getName(); 351 if (HFileLink.isHFileLink(hfile)) { 352 names.add(HFileLink.getReferencedHFileName(hfile)); 353 } else if (StoreFileInfo.isReference(hfile)) { 354 Path refPath = StoreFileInfo.getReferredToFile(new Path(new Path( 355 new Path(new Path(regionInfo.getTable().getNamespaceAsString(), 356 regionInfo.getTable().getQualifierAsString()), regionInfo.getEncodedName()), 357 family), hfile)); 358 names.add(hfile); 359 names.add(refPath.getName()); 360 if (HFileLink.isHFileLink(refPath.getName())) { 361 names.add(HFileLink.getReferencedHFileName(refPath.getName())); 362 } 363 } else { 364 names.add(hfile); 365 } 366 } 367 }); 368 return names; 369 } 370}