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}