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.procedure;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertTrue;
022
023import java.util.List;
024import org.apache.hadoop.conf.Configuration;
025import org.apache.hadoop.fs.FileSystem;
026import org.apache.hadoop.fs.Path;
027import org.apache.hadoop.hbase.HBaseTestingUtil;
028import org.apache.hadoop.hbase.ServerName;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.client.RegionInfo;
031import org.apache.hadoop.hbase.client.SnapshotDescription;
032import org.apache.hadoop.hbase.client.SnapshotType;
033import org.apache.hadoop.hbase.client.Table;
034import org.apache.hadoop.hbase.master.HMaster;
035import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
036import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
037import org.apache.hadoop.hbase.procedure2.RemoteProcedureDispatcher;
038import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
039import org.apache.hadoop.hbase.snapshot.SnapshotManifestV2;
040import org.apache.hadoop.hbase.testclassification.LargeTests;
041import org.apache.hadoop.hbase.testclassification.MasterTests;
042import org.apache.hadoop.hbase.util.Bytes;
043import org.apache.hadoop.hbase.util.CommonFSUtils;
044import org.apache.hadoop.hbase.util.Pair;
045import org.apache.hadoop.hbase.util.RegionSplitter;
046import org.junit.jupiter.api.AfterEach;
047import org.junit.jupiter.api.BeforeEach;
048import org.junit.jupiter.api.Tag;
049import org.junit.jupiter.api.Test;
050import org.slf4j.Logger;
051import org.slf4j.LoggerFactory;
052
053import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
054import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;
055
056@Tag(MasterTests.TAG)
057@Tag(LargeTests.TAG)
058public class TestSnapshotRegionProcedure {
059  private static final Logger LOG = LoggerFactory.getLogger(TestSnapshotRegionProcedure.class);
060
061  private static HBaseTestingUtil TEST_UTIL;
062  private HMaster master;
063  private TableName tableName;
064  private SnapshotProtos.SnapshotDescription snapshotProto;
065  private Path workingDir;
066  private FileSystem workingDirFs;
067
068  @BeforeEach
069  public void setup() throws Exception {
070    TEST_UTIL = new HBaseTestingUtil();
071    Configuration conf = TEST_UTIL.getConfiguration();
072    // disable info server. Info server is useful when we run unit tests locally, but it will
073    // fails integration testing of jenkins.
074    // conf.setInt(HConstants.MASTER_INFO_PORT, 8080);
075
076    // delay dispatch so that we can do something, for example kill a target server
077    conf.setInt(RemoteProcedureDispatcher.DISPATCH_DELAY_CONF_KEY, 10000);
078    conf.setInt(RemoteProcedureDispatcher.DISPATCH_MAX_QUEUE_SIZE_CONF_KEY, 128);
079    TEST_UTIL.startMiniCluster(3);
080    master = TEST_UTIL.getHBaseCluster().getMaster();
081    tableName = TableName.valueOf(Bytes.toBytes("SRPTestTable"));
082    byte[] cf = Bytes.toBytes("cf");
083    String SNAPSHOT_NAME = "SnapshotRegionProcedureTest";
084    SnapshotDescription snapshot =
085      new SnapshotDescription(SNAPSHOT_NAME, tableName, SnapshotType.FLUSH);
086    snapshotProto = ProtobufUtil.createHBaseProtosSnapshotDesc(snapshot);
087    snapshotProto = SnapshotDescriptionUtils.validate(snapshotProto, master.getConfiguration());
088    final byte[][] splitKeys = new RegionSplitter.HexStringSplit().split(10);
089    Table table = TEST_UTIL.createTable(tableName, cf, splitKeys);
090    TEST_UTIL.loadTable(table, cf, false);
091    Path rootDir = CommonFSUtils.getRootDir(conf);
092    this.workingDir = SnapshotDescriptionUtils.getWorkingSnapshotDir(snapshotProto, rootDir, conf);
093    this.workingDirFs = workingDir.getFileSystem(conf);
094    if (!workingDirFs.exists(workingDir)) {
095      workingDirFs.mkdirs(workingDir);
096    }
097  }
098
099  private boolean assertRegionManifestGenerated(RegionInfo region) throws Exception {
100    // path: /<root dir>/<snapshot dir>/<working dir>/<snapshot name>/region-manifest.<encode name>
101    String regionManifest = SnapshotManifestV2.SNAPSHOT_MANIFEST_PREFIX + region.getEncodedName();
102    Path targetPath = new Path(workingDir, regionManifest);
103    return workingDirFs.exists(targetPath);
104  }
105
106  @Test
107  public void testSimpleSnapshotRegion() throws Exception {
108    ProcedureExecutor<MasterProcedureEnv> procExec = master.getMasterProcedureExecutor();
109    List<Pair<RegionInfo, ServerName>> regions =
110      master.getAssignmentManager().getTableRegionsAndLocations(tableName, true);
111    assertEquals(10, regions.size());
112    Pair<RegionInfo, ServerName> region = regions.get(0);
113    SnapshotRegionProcedure srp = new SnapshotRegionProcedure(snapshotProto, region.getFirst());
114    long procId = procExec.submitProcedure(srp);
115    ProcedureTestingUtility.waitProcedure(procExec, procId);
116    assertTrue(assertRegionManifestGenerated(region.getFirst()));
117  }
118
119  @Test
120  public void testRegionServerCrashWhileTakingSnapshotRegion() throws Exception {
121    ProcedureExecutor<MasterProcedureEnv> procExec = master.getMasterProcedureExecutor();
122    List<Pair<RegionInfo, ServerName>> regions =
123      master.getAssignmentManager().getTableRegionsAndLocations(tableName, true);
124    assertEquals(10, regions.size());
125    Pair<RegionInfo, ServerName> pair = regions.get(0);
126    SnapshotRegionProcedure srp = new SnapshotRegionProcedure(snapshotProto, pair.getFirst());
127    long procId = procExec.submitProcedure(srp);
128    TEST_UTIL.getHBaseCluster().killRegionServer(pair.getSecond());
129    TEST_UTIL.waitFor(60000, () -> !pair.getSecond().equals(master.getAssignmentManager()
130      .getRegionStates().getRegionStateNode(pair.getFirst()).getRegionLocation()));
131    TEST_UTIL.waitFor(60000, () -> srp.inRetrying());
132    ProcedureTestingUtility.waitProcedure(procExec, procId);
133    assertTrue(assertRegionManifestGenerated(pair.getFirst()));
134  }
135
136  @AfterEach
137  public void teardown() throws Exception {
138    if (this.master != null) {
139      ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(master.getMasterProcedureExecutor(),
140        false);
141    }
142    TEST_UTIL.shutdownMiniCluster();
143  }
144}