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.IOException; 022import java.io.InterruptedIOException; 023import java.util.ArrayList; 024import java.util.Collection; 025import java.util.List; 026import java.util.concurrent.Callable; 027import java.util.concurrent.ExecutionException; 028import java.util.concurrent.Executor; 029import java.util.concurrent.ExecutorCompletionService; 030 031import org.apache.hadoop.conf.Configuration; 032import org.apache.hadoop.fs.FileStatus; 033import org.apache.hadoop.fs.FileSystem; 034import org.apache.hadoop.fs.Path; 035import org.apache.hadoop.hbase.client.RegionInfo; 036import org.apache.hadoop.hbase.regionserver.HRegionFileSystem; 037import org.apache.hadoop.hbase.regionserver.StoreFileInfo; 038import org.apache.hadoop.hbase.util.Bytes; 039import org.apache.hadoop.hbase.util.FSUtils; 040import org.apache.yetus.audience.InterfaceAudience; 041import org.slf4j.Logger; 042import org.slf4j.LoggerFactory; 043import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations; 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 * DO NOT USE DIRECTLY. USE {@link SnapshotManifest}. 050 * 051 * Snapshot v1 layout format 052 * - Each region in the table is represented by a directory with the .hregioninfo file 053 * /snapshotName/regionName/.hregioninfo 054 * - Each file present in the table is represented by an empty file 055 * /snapshotName/regionName/familyName/fileName 056 */ 057@InterfaceAudience.Private 058public final class SnapshotManifestV1 { 059 private static final Logger LOG = LoggerFactory.getLogger(SnapshotManifestV1.class); 060 061 public static final int DESCRIPTOR_VERSION = 0; 062 063 private SnapshotManifestV1() { 064 } 065 066 static class ManifestBuilder implements SnapshotManifest.RegionVisitor< 067 HRegionFileSystem, Path> { 068 private final Configuration conf; 069 private final Path snapshotDir; 070 private final FileSystem rootFs; 071 private final FileSystem workingDirFs; 072 073 public ManifestBuilder(final Configuration conf, final FileSystem rootFs, 074 final Path snapshotDir) throws IOException { 075 this.snapshotDir = snapshotDir; 076 this.conf = conf; 077 this.rootFs = rootFs; 078 this.workingDirFs = snapshotDir.getFileSystem(conf); 079 } 080 081 @Override 082 public HRegionFileSystem regionOpen(final RegionInfo regionInfo) throws IOException { 083 HRegionFileSystem snapshotRegionFs = HRegionFileSystem.createRegionOnFileSystem(conf, 084 workingDirFs, snapshotDir, regionInfo); 085 return snapshotRegionFs; 086 } 087 088 @Override 089 public void regionClose(final HRegionFileSystem region) { 090 } 091 092 @Override 093 public Path familyOpen(final HRegionFileSystem snapshotRegionFs, final byte[] familyName) { 094 Path familyDir = snapshotRegionFs.getStoreDir(Bytes.toString(familyName)); 095 return familyDir; 096 } 097 098 @Override 099 public void familyClose(final HRegionFileSystem region, final Path family) { 100 } 101 102 @Override 103 public void storeFile(final HRegionFileSystem region, final Path familyDir, 104 final StoreFileInfo storeFile) throws IOException { 105 Path referenceFile = new Path(familyDir, storeFile.getPath().getName()); 106 boolean success = true; 107 if (storeFile.isReference()) { 108 // write the Reference object to the snapshot 109 storeFile.getReference().write(workingDirFs, referenceFile); 110 } else { 111 // create "reference" to this store file. It is intentionally an empty file -- all 112 // necessary information is captured by its fs location and filename. This allows us to 113 // only figure out what needs to be done via a single nn operation (instead of having to 114 // open and read the files as well). 115 success = workingDirFs.createNewFile(referenceFile); 116 } 117 if (!success) { 118 throw new IOException("Failed to create reference file:" + referenceFile); 119 } 120 } 121 } 122 123 static List<SnapshotRegionManifest> loadRegionManifests(final Configuration conf, 124 final Executor executor,final FileSystem fs, final Path snapshotDir, 125 final SnapshotDescription desc) throws IOException { 126 FileStatus[] regions = FSUtils.listStatus(fs, snapshotDir, new FSUtils.RegionDirFilter(fs)); 127 if (regions == null) { 128 LOG.debug("No regions under directory:" + snapshotDir); 129 return null; 130 } 131 132 final ExecutorCompletionService<SnapshotRegionManifest> completionService = 133 new ExecutorCompletionService<>(executor); 134 for (final FileStatus region: regions) { 135 completionService.submit(new Callable<SnapshotRegionManifest>() { 136 @Override 137 public SnapshotRegionManifest call() throws IOException { 138 RegionInfo hri = HRegionFileSystem.loadRegionInfoFileContent(fs, region.getPath()); 139 return buildManifestFromDisk(conf, fs, snapshotDir, hri); 140 } 141 }); 142 } 143 144 ArrayList<SnapshotRegionManifest> regionsManifest = new ArrayList<>(regions.length); 145 try { 146 for (int i = 0; i < regions.length; ++i) { 147 regionsManifest.add(completionService.take().get()); 148 } 149 } catch (InterruptedException e) { 150 throw new InterruptedIOException(e.getMessage()); 151 } catch (ExecutionException e) { 152 IOException ex = new IOException(); 153 ex.initCause(e.getCause()); 154 throw ex; 155 } 156 return regionsManifest; 157 } 158 159 static void deleteRegionManifest(final FileSystem fs, final Path snapshotDir, 160 final SnapshotRegionManifest manifest) throws IOException { 161 String regionName = SnapshotManifest.getRegionNameFromManifest(manifest); 162 fs.delete(new Path(snapshotDir, regionName), true); 163 } 164 165 static SnapshotRegionManifest buildManifestFromDisk(final Configuration conf, 166 final FileSystem fs, final Path tableDir, final RegionInfo regionInfo) throws IOException { 167 HRegionFileSystem regionFs = HRegionFileSystem.openRegionFromFileSystem(conf, fs, 168 tableDir, regionInfo, true); 169 SnapshotRegionManifest.Builder manifest = SnapshotRegionManifest.newBuilder(); 170 171 // 1. dump region meta info into the snapshot directory 172 LOG.debug("Storing region-info for snapshot."); 173 manifest.setRegionInfo(ProtobufUtil.toRegionInfo(regionInfo)); 174 175 // 2. iterate through all the stores in the region 176 LOG.debug("Creating references for hfiles"); 177 178 // This ensures that we have an atomic view of the directory as long as we have < ls limit 179 // (batch size of the files in a directory) on the namenode. Otherwise, we get back the files in 180 // batches and may miss files being added/deleted. This could be more robust (iteratively 181 // checking to see if we have all the files until we are sure), but the limit is currently 1000 182 // files/batch, far more than the number of store files under a single column family. 183 Collection<String> familyNames = regionFs.getFamilies(); 184 if (familyNames != null) { 185 for (String familyName: familyNames) { 186 Collection<StoreFileInfo> storeFiles = regionFs.getStoreFiles(familyName, false); 187 if (storeFiles == null) { 188 LOG.debug("No files under family: " + familyName); 189 continue; 190 } 191 192 // 2.1. build the snapshot reference for the store 193 SnapshotRegionManifest.FamilyFiles.Builder family = 194 SnapshotRegionManifest.FamilyFiles.newBuilder(); 195 family.setFamilyName(UnsafeByteOperations.unsafeWrap(Bytes.toBytes(familyName))); 196 197 if (LOG.isDebugEnabled()) { 198 LOG.debug("Adding snapshot references for " + storeFiles + " hfiles"); 199 } 200 201 // 2.2. iterate through all the store's files and create "references". 202 int i = 0; 203 int sz = storeFiles.size(); 204 for (StoreFileInfo storeFile: storeFiles) { 205 // create "reference" to this store file. 206 LOG.debug("Adding reference for file ("+ (++i) +"/" + sz + "): " + storeFile.getPath()); 207 SnapshotRegionManifest.StoreFile.Builder sfManifest = 208 SnapshotRegionManifest.StoreFile.newBuilder(); 209 sfManifest.setName(storeFile.getPath().getName()); 210 family.addStoreFiles(sfManifest.build()); 211 } 212 manifest.addFamilyFiles(family.build()); 213 } 214 } 215 return manifest.build(); 216 } 217}