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