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