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