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 */ 018package org.apache.hadoop.hbase.snapshot; 019 020import java.io.FileNotFoundException; 021import java.io.IOException; 022import java.io.InterruptedIOException; 023import java.util.HashSet; 024import java.util.List; 025import java.util.Set; 026import java.util.concurrent.Callable; 027import java.util.concurrent.ExecutionException; 028import java.util.concurrent.ExecutorCompletionService; 029import java.util.concurrent.ExecutorService; 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.fs.FileStatus; 032import org.apache.hadoop.fs.FileSystem; 033import org.apache.hadoop.fs.Path; 034import org.apache.hadoop.hbase.TableName; 035import org.apache.hadoop.hbase.client.RegionInfo; 036import org.apache.hadoop.hbase.io.HFileLink; 037import org.apache.hadoop.hbase.mob.MobUtils; 038import org.apache.hadoop.hbase.regionserver.StoreFileInfo; 039import org.apache.hadoop.hbase.util.HFileArchiveUtil; 040import org.apache.yetus.audience.InterfaceAudience; 041import org.slf4j.Logger; 042import org.slf4j.LoggerFactory; 043 044import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 045import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotDescription; 046import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotRegionManifest; 047 048/** 049 * Utility methods for interacting with the snapshot referenced files. 050 */ 051@InterfaceAudience.Private 052public final class SnapshotReferenceUtil { 053 private static final Logger LOG = LoggerFactory.getLogger(SnapshotReferenceUtil.class); 054 055 public interface StoreFileVisitor { 056 void storeFile(final RegionInfo regionInfo, final String familyName, 057 final SnapshotRegionManifest.StoreFile storeFile) throws IOException; 058 } 059 060 public interface SnapshotVisitor extends StoreFileVisitor { 061 } 062 063 private SnapshotReferenceUtil() { 064 // private constructor for utility class 065 } 066 067 /** 068 * Iterate over the snapshot store files 069 * @param conf The current {@link Configuration} instance. 070 * @param fs {@link FileSystem} 071 * @param snapshotDir {@link Path} to the Snapshot directory 072 * @param visitor callback object to get the referenced files 073 * @throws IOException if an error occurred while scanning the directory 074 */ 075 public static void visitReferencedFiles(final Configuration conf, final FileSystem fs, 076 final Path snapshotDir, final SnapshotVisitor visitor) throws IOException { 077 SnapshotDescription desc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir); 078 visitReferencedFiles(conf, fs, snapshotDir, desc, visitor); 079 } 080 081 /** 082 * Iterate over the snapshot store files, restored.edits and logs 083 * @param conf The current {@link Configuration} instance. 084 * @param fs {@link FileSystem} 085 * @param snapshotDir {@link Path} to the Snapshot directory 086 * @param desc the {@link SnapshotDescription} of the snapshot to verify 087 * @param visitor callback object to get the referenced files 088 * @throws IOException if an error occurred while scanning the directory 089 */ 090 public static void visitReferencedFiles(final Configuration conf, final FileSystem fs, 091 final Path snapshotDir, final SnapshotDescription desc, final SnapshotVisitor visitor) 092 throws IOException { 093 visitTableStoreFiles(conf, fs, snapshotDir, desc, visitor); 094 } 095 096 /** 097 * © Iterate over the snapshot store files 098 * @param conf The current {@link Configuration} instance. 099 * @param fs {@link FileSystem} 100 * @param snapshotDir {@link Path} to the Snapshot directory 101 * @param desc the {@link SnapshotDescription} of the snapshot to verify 102 * @param visitor callback object to get the store files 103 * @throws IOException if an error occurred while scanning the directory 104 */ 105 static void visitTableStoreFiles(final Configuration conf, final FileSystem fs, 106 final Path snapshotDir, final SnapshotDescription desc, final StoreFileVisitor visitor) 107 throws IOException { 108 SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, desc); 109 List<SnapshotRegionManifest> regionManifests = manifest.getRegionManifests(); 110 if (regionManifests == null || regionManifests.isEmpty()) { 111 LOG.debug("No manifest files present: " + snapshotDir); 112 return; 113 } 114 115 for (SnapshotRegionManifest regionManifest : regionManifests) { 116 visitRegionStoreFiles(regionManifest, visitor); 117 } 118 } 119 120 /** 121 * Iterate over the snapshot store files in the specified region 122 * @param manifest snapshot manifest to inspect 123 * @param visitor callback object to get the store files 124 * @throws IOException if an error occurred while scanning the directory 125 */ 126 public static void visitRegionStoreFiles(final SnapshotRegionManifest manifest, 127 final StoreFileVisitor visitor) throws IOException { 128 RegionInfo regionInfo = ProtobufUtil.toRegionInfo(manifest.getRegionInfo()); 129 for (SnapshotRegionManifest.FamilyFiles familyFiles : manifest.getFamilyFilesList()) { 130 String familyName = familyFiles.getFamilyName().toStringUtf8(); 131 for (SnapshotRegionManifest.StoreFile storeFile : familyFiles.getStoreFilesList()) { 132 visitor.storeFile(regionInfo, familyName, storeFile); 133 } 134 } 135 } 136 137 /** 138 * Verify the validity of the snapshot 139 * @param conf The current {@link Configuration} instance. 140 * @param fs {@link FileSystem} 141 * @param snapshotDir {@link Path} to the Snapshot directory of the snapshot to verify 142 * @param snapshotDesc the {@link SnapshotDescription} of the snapshot to verify 143 * @throws CorruptedSnapshotException if the snapshot is corrupted 144 * @throws IOException if an error occurred while scanning the directory 145 */ 146 public static void verifySnapshot(final Configuration conf, final FileSystem fs, 147 final Path snapshotDir, final SnapshotDescription snapshotDesc) throws IOException { 148 SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, snapshotDesc); 149 verifySnapshot(conf, fs, manifest); 150 } 151 152 /** 153 * Verify the validity of the snapshot 154 * @param conf The current {@link Configuration} instance. 155 * @param fs {@link FileSystem} 156 * @param manifest snapshot manifest to inspect 157 * @throws CorruptedSnapshotException if the snapshot is corrupted 158 * @throws IOException if an error occurred while scanning the directory 159 */ 160 public static void verifySnapshot(final Configuration conf, final FileSystem fs, 161 final SnapshotManifest manifest) throws IOException { 162 final SnapshotDescription snapshotDesc = manifest.getSnapshotDescription(); 163 final Path snapshotDir = manifest.getSnapshotDir(); 164 concurrentVisitReferencedFiles(conf, fs, manifest, "VerifySnapshot", new StoreFileVisitor() { 165 @Override 166 public void storeFile(final RegionInfo regionInfo, final String family, 167 final SnapshotRegionManifest.StoreFile storeFile) throws IOException { 168 verifyStoreFile(conf, fs, snapshotDir, snapshotDesc, regionInfo, family, storeFile); 169 } 170 }); 171 } 172 173 /** 174 * Verify the validity of the snapshot. 175 * @param visitor user-specified store file visitor 176 */ 177 public static void verifySnapshot(final Configuration conf, final FileSystem fs, 178 final SnapshotManifest manifest, final StoreFileVisitor visitor) throws IOException { 179 concurrentVisitReferencedFiles(conf, fs, manifest, "VerifySnapshot", visitor); 180 } 181 182 public static void concurrentVisitReferencedFiles(final Configuration conf, final FileSystem fs, 183 final SnapshotManifest manifest, final String desc, final StoreFileVisitor visitor) 184 throws IOException { 185 186 final Path snapshotDir = manifest.getSnapshotDir(); 187 List<SnapshotRegionManifest> regionManifests = manifest.getRegionManifests(); 188 if (regionManifests == null || regionManifests.isEmpty()) { 189 LOG.debug("No manifest files present: " + snapshotDir); 190 return; 191 } 192 193 ExecutorService exec = SnapshotManifest.createExecutor(conf, desc); 194 195 try { 196 concurrentVisitReferencedFiles(conf, fs, manifest, exec, visitor); 197 } finally { 198 exec.shutdown(); 199 } 200 } 201 202 public static void concurrentVisitReferencedFiles(final Configuration conf, final FileSystem fs, 203 final SnapshotManifest manifest, final ExecutorService exec, final StoreFileVisitor visitor) 204 throws IOException { 205 final SnapshotDescription snapshotDesc = manifest.getSnapshotDescription(); 206 final Path snapshotDir = manifest.getSnapshotDir(); 207 208 List<SnapshotRegionManifest> regionManifests = manifest.getRegionManifests(); 209 if (regionManifests == null || regionManifests.isEmpty()) { 210 LOG.debug("No manifest files present: " + snapshotDir); 211 return; 212 } 213 214 final ExecutorCompletionService<Void> completionService = new ExecutorCompletionService<>(exec); 215 216 for (final SnapshotRegionManifest regionManifest : regionManifests) { 217 completionService.submit(new Callable<Void>() { 218 @Override 219 public Void call() throws IOException { 220 visitRegionStoreFiles(regionManifest, visitor); 221 return null; 222 } 223 }); 224 } 225 try { 226 for (int i = 0; i < regionManifests.size(); ++i) { 227 completionService.take().get(); 228 } 229 } catch (InterruptedException e) { 230 throw new InterruptedIOException(e.getMessage()); 231 } catch (ExecutionException e) { 232 if (e.getCause() instanceof CorruptedSnapshotException) { 233 throw new CorruptedSnapshotException(e.getCause().getMessage(), 234 ProtobufUtil.createSnapshotDesc(snapshotDesc)); 235 } else { 236 throw new IOException(e.getCause()); 237 } 238 } 239 } 240 241 /** 242 * Verify the validity of the snapshot store file 243 * @param conf The current {@link Configuration} instance. 244 * @param fs {@link FileSystem} 245 * @param snapshotDir {@link Path} to the Snapshot directory of the snapshot to verify 246 * @param snapshot the {@link SnapshotDescription} of the snapshot to verify 247 * @param regionInfo {@link RegionInfo} of the region that contains the store file 248 * @param family family that contains the store file 249 * @param storeFile the store file to verify 250 * @throws CorruptedSnapshotException if the snapshot is corrupted 251 * @throws IOException if an error occurred while scanning the directory 252 */ 253 public static void verifyStoreFile(final Configuration conf, final FileSystem fs, 254 final Path snapshotDir, final SnapshotDescription snapshot, final RegionInfo regionInfo, 255 final String family, final SnapshotRegionManifest.StoreFile storeFile) throws IOException { 256 TableName table = TableName.valueOf(snapshot.getTable()); 257 String fileName = storeFile.getName(); 258 259 Path refPath = null; 260 if (StoreFileInfo.isReference(fileName)) { 261 // If is a reference file check if the parent file is present in the snapshot 262 refPath = new Path(new Path(regionInfo.getEncodedName(), family), fileName); 263 refPath = StoreFileInfo.getReferredToFile(refPath); 264 String refRegion = refPath.getParent().getParent().getName(); 265 refPath = HFileLink.createPath(table, refRegion, family, refPath.getName()); 266 if (!HFileLink.buildFromHFileLinkPattern(conf, refPath).exists(fs)) { 267 throw new CorruptedSnapshotException( 268 "Missing parent hfile for: " + fileName + " path=" + refPath, 269 ProtobufUtil.createSnapshotDesc(snapshot)); 270 } 271 272 if (storeFile.hasReference()) { 273 // We don't really need to look for the file on-disk 274 // we already have the Reference information embedded here. 275 return; 276 } 277 } 278 279 Path linkPath; 280 if (refPath != null && HFileLink.isHFileLink(refPath)) { 281 linkPath = new Path(family, refPath.getName()); 282 } else if (HFileLink.isHFileLink(fileName)) { 283 linkPath = new Path(family, fileName); 284 } else { 285 linkPath = new Path(family, 286 HFileLink.createHFileLinkName(table, regionInfo.getEncodedName(), fileName)); 287 } 288 289 // check if the linked file exists (in the archive, or in the table dir) 290 HFileLink link = null; 291 if (MobUtils.isMobRegionInfo(regionInfo)) { 292 // for mob region 293 link = HFileLink.buildFromHFileLinkPattern(MobUtils.getQualifiedMobRootDir(conf), 294 HFileArchiveUtil.getArchivePath(conf), linkPath); 295 } else { 296 // not mob region 297 link = HFileLink.buildFromHFileLinkPattern(conf, linkPath); 298 } 299 try { 300 FileStatus fstat = link.getFileStatus(fs); 301 if (storeFile.hasFileSize() && storeFile.getFileSize() != fstat.getLen()) { 302 String msg = "hfile: " + fileName + " size does not match with the expected one. " 303 + " found=" + fstat.getLen() + " expected=" + storeFile.getFileSize(); 304 LOG.error(msg); 305 throw new CorruptedSnapshotException(msg, ProtobufUtil.createSnapshotDesc(snapshot)); 306 } 307 } catch (FileNotFoundException e) { 308 String msg = "Can't find hfile: " + fileName + " in the real (" + link.getOriginPath() 309 + ") or archive (" + link.getArchivePath() + ") directory for the primary table."; 310 LOG.error(msg); 311 throw new CorruptedSnapshotException(msg, ProtobufUtil.createSnapshotDesc(snapshot)); 312 } 313 } 314 315 /** 316 * Returns the store file names in the snapshot. 317 * @param conf The current {@link Configuration} instance. 318 * @param fs {@link FileSystem} 319 * @param snapshotDir {@link Path} to the Snapshot directory 320 * @throws IOException if an error occurred while scanning the directory 321 * @return the names of hfiles in the specified snaphot 322 */ 323 public static Set<String> getHFileNames(final Configuration conf, final FileSystem fs, 324 final Path snapshotDir) throws IOException { 325 SnapshotDescription desc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir); 326 return getHFileNames(conf, fs, snapshotDir, desc); 327 } 328 329 /** 330 * Returns the store file names in the snapshot. 331 * @param conf The current {@link Configuration} instance. 332 * @param fs {@link FileSystem} 333 * @param snapshotDir {@link Path} to the Snapshot directory 334 * @param snapshotDesc the {@link SnapshotDescription} of the snapshot to inspect 335 * @throws IOException if an error occurred while scanning the directory 336 * @return the names of hfiles in the specified snaphot 337 */ 338 private static Set<String> getHFileNames(final Configuration conf, final FileSystem fs, 339 final Path snapshotDir, final SnapshotDescription snapshotDesc) throws IOException { 340 final Set<String> names = new HashSet<>(); 341 visitTableStoreFiles(conf, fs, snapshotDir, snapshotDesc, new StoreFileVisitor() { 342 @Override 343 public void storeFile(final RegionInfo regionInfo, final String family, 344 final SnapshotRegionManifest.StoreFile storeFile) throws IOException { 345 String hfile = storeFile.getName(); 346 if (HFileLink.isHFileLink(hfile)) { 347 names.add(HFileLink.getReferencedHFileName(hfile)); 348 } else if (StoreFileInfo.isReference(hfile)) { 349 Path refPath = 350 StoreFileInfo.getReferredToFile(new Path(new Path( 351 new Path(new Path(regionInfo.getTable().getNamespaceAsString(), 352 regionInfo.getTable().getQualifierAsString()), regionInfo.getEncodedName()), 353 family), hfile)); 354 names.add(hfile); 355 names.add(refPath.getName()); 356 if (HFileLink.isHFileLink(refPath.getName())) { 357 names.add(HFileLink.getReferencedHFileName(refPath.getName())); 358 } 359 } else { 360 names.add(hfile); 361 } 362 } 363 }); 364 return names; 365 } 366}