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.master.snapshot; 019 020import java.io.IOException; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Set; 024import java.util.concurrent.ThreadPoolExecutor; 025import org.apache.hadoop.hbase.ServerName; 026import org.apache.hadoop.hbase.client.RegionInfo; 027import org.apache.hadoop.hbase.client.RegionReplicaUtil; 028import org.apache.hadoop.hbase.errorhandling.ForeignException; 029import org.apache.hadoop.hbase.master.MasterServices; 030import org.apache.hadoop.hbase.mob.MobUtils; 031import org.apache.hadoop.hbase.snapshot.ClientSnapshotDescriptionUtils; 032import org.apache.hadoop.hbase.snapshot.SnapshotManifest; 033import org.apache.hadoop.hbase.util.CommonFSUtils; 034import org.apache.hadoop.hbase.util.ModifyRegionUtils; 035import org.apache.hadoop.hbase.util.Pair; 036import org.apache.yetus.audience.InterfaceAudience; 037import org.apache.yetus.audience.InterfaceStability; 038import org.apache.zookeeper.KeeperException; 039import org.slf4j.Logger; 040import org.slf4j.LoggerFactory; 041 042import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotDescription; 043 044/** 045 * Take a snapshot of a disabled table. 046 * <p> 047 * Table must exist when taking the snapshot, or results are undefined. 048 */ 049@InterfaceAudience.Private 050@InterfaceStability.Evolving 051public class DisabledTableSnapshotHandler extends TakeSnapshotHandler { 052 private static final Logger LOG = LoggerFactory.getLogger(DisabledTableSnapshotHandler.class); 053 054 /** 055 * @param snapshot descriptor of the snapshot to take 056 * @param masterServices master services provider 057 * @throws IOException if it cannot access the filesystem of the snapshot temporary directory 058 */ 059 public DisabledTableSnapshotHandler(SnapshotDescription snapshot, 060 final MasterServices masterServices, final SnapshotManager snapshotManager) throws IOException { 061 super(snapshot, masterServices, snapshotManager); 062 } 063 064 @Override 065 public DisabledTableSnapshotHandler prepare() throws Exception { 066 return (DisabledTableSnapshotHandler) super.prepare(); 067 } 068 069 // TODO consider parallelizing these operations since they are independent. Right now its just 070 // easier to keep them serial though 071 @Override 072 public void snapshotRegions(List<Pair<RegionInfo, ServerName>> regionsAndLocations) 073 throws IOException, KeeperException { 074 try { 075 // 1. get all the regions hosting this table. 076 077 // extract each pair to separate lists 078 Set<RegionInfo> regions = new HashSet<>(); 079 for (Pair<RegionInfo, ServerName> p : regionsAndLocations) { 080 // Don't include non-default regions 081 RegionInfo hri = p.getFirst(); 082 if (RegionReplicaUtil.isDefaultReplica(hri)) { 083 regions.add(hri); 084 } 085 } 086 // handle the mob files if any. 087 boolean mobEnabled = MobUtils.hasMobColumns(htd); 088 if (mobEnabled) { 089 // snapshot the mob files as a offline region. 090 RegionInfo mobRegionInfo = MobUtils.getMobRegionInfo(htd.getTableName()); 091 regions.add(mobRegionInfo); 092 } 093 094 // 2. for each region, write all the info to disk 095 String msg = "Starting to write region info and WALs for regions for offline snapshot:" 096 + ClientSnapshotDescriptionUtils.toString(snapshot); 097 LOG.info(msg); 098 status.setStatus(msg); 099 100 ThreadPoolExecutor exec = SnapshotManifest.createExecutor(conf, "DisabledTableSnapshot"); 101 try { 102 ModifyRegionUtils.editRegions(exec, regions, new ModifyRegionUtils.RegionEditTask() { 103 @Override 104 public void editRegion(final RegionInfo regionInfo) throws IOException { 105 snapshotManifest.addRegion(CommonFSUtils.getTableDir(rootDir, snapshotTable), 106 regionInfo); 107 } 108 }); 109 } finally { 110 exec.shutdown(); 111 } 112 } catch (Exception e) { 113 // make sure we capture the exception to propagate back to the client later 114 String reason = "Failed snapshot " + ClientSnapshotDescriptionUtils.toString(snapshot) 115 + " due to exception:" + e.getMessage(); 116 ForeignException ee = new ForeignException(reason, e); 117 monitor.receive(ee); 118 status.abort("Snapshot of table: " + snapshotTable + " failed because " + e.getMessage()); 119 } finally { 120 LOG.debug( 121 "Marking snapshot" + ClientSnapshotDescriptionUtils.toString(snapshot) + " as finished."); 122 } 123 } 124 125 @Override 126 protected boolean downgradeToSharedTableLock() { 127 // for taking snapshot on disabled table, it is OK to always hold the exclusive lock, as we do 128 // not need to assign the regions when there are region server crashes. 129 return false; 130 } 131}