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