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.Assert.assertTrue;
021
022import java.util.List;
023import java.util.stream.Stream;
024import org.apache.hadoop.hbase.HBaseClassTestRule;
025import org.apache.hadoop.hbase.TableExistsException;
026import org.apache.hadoop.hbase.TableName;
027import org.apache.hadoop.hbase.client.Admin;
028import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
029import org.apache.hadoop.hbase.client.SnapshotDescription;
030import org.apache.hadoop.hbase.client.TableDescriptor;
031import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
032import org.apache.hadoop.hbase.procedure2.Procedure;
033import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
034import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
035import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils;
036import org.apache.hadoop.hbase.testclassification.MasterTests;
037import org.apache.hadoop.hbase.testclassification.MediumTests;
038import org.apache.hadoop.hbase.util.Bytes;
039import org.junit.After;
040import org.junit.ClassRule;
041import org.junit.Test;
042import org.junit.experimental.categories.Category;
043import org.slf4j.Logger;
044import org.slf4j.LoggerFactory;
045
046import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
047import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos;
048
049@Category({MasterTests.class, MediumTests.class})
050public class TestCloneSnapshotProcedure extends TestTableDDLProcedureBase {
051
052  @ClassRule
053  public static final HBaseClassTestRule CLASS_RULE =
054      HBaseClassTestRule.forClass(TestCloneSnapshotProcedure.class);
055
056  private static final Logger LOG = LoggerFactory.getLogger(TestCloneSnapshotProcedure.class);
057
058  protected final byte[] CF = Bytes.toBytes("cf1");
059
060  private static SnapshotProtos.SnapshotDescription snapshot = null;
061
062  @After
063  @Override
064  public void tearDown() throws Exception {
065    super.tearDown();
066    SnapshotTestingUtils.deleteAllSnapshots(UTIL.getAdmin());
067    snapshot = null;
068  }
069
070  private SnapshotProtos.SnapshotDescription getSnapshot() throws Exception {
071    if (snapshot == null) {
072      final TableName snapshotTableName = TableName.valueOf("testCloneSnapshot");
073      long tid = System.currentTimeMillis();
074      final byte[] snapshotName = Bytes.toBytes("snapshot-" + tid);
075
076      Admin admin = UTIL.getAdmin();
077      // create Table
078      SnapshotTestingUtils.createTable(UTIL, snapshotTableName, getNumReplicas(), CF);
079      // Load data
080      SnapshotTestingUtils.loadData(UTIL, snapshotTableName, 500, CF);
081      admin.disableTable(snapshotTableName);
082      // take a snapshot
083      admin.snapshot(snapshotName, snapshotTableName);
084      admin.enableTable(snapshotTableName);
085
086      List<SnapshotDescription> snapshotList = admin.listSnapshots();
087      snapshot = ProtobufUtil.createHBaseProtosSnapshotDesc(snapshotList.get(0));
088    }
089    return snapshot;
090  }
091
092  private int getNumReplicas() {
093    return 1;
094  }
095
096  public static TableDescriptor createTableDescriptor(TableName tableName, byte[]... family) {
097    TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName);
098    Stream.of(family).map(ColumnFamilyDescriptorBuilder::of)
099      .forEachOrdered(builder::setColumnFamily);
100    return builder.build();
101  }
102
103  @Test
104  public void testCloneSnapshot() throws Exception {
105    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
106    final TableName clonedTableName = TableName.valueOf("testCloneSnapshot2");
107    final TableDescriptor htd = createTableDescriptor(clonedTableName, CF);
108
109    // take the snapshot
110    SnapshotProtos.SnapshotDescription snapshotDesc = getSnapshot();
111
112    long procId = ProcedureTestingUtility.submitAndWait(
113      procExec, new CloneSnapshotProcedure(procExec.getEnvironment(), htd, snapshotDesc));
114    ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId));
115    MasterProcedureTestingUtility.validateTableIsEnabled(
116      UTIL.getHBaseCluster().getMaster(),
117      clonedTableName);
118  }
119
120  @Test
121  public void testCloneSnapshotToSameTable() throws Exception {
122    // take the snapshot
123    SnapshotProtos.SnapshotDescription snapshotDesc = getSnapshot();
124
125    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
126    final TableName clonedTableName = TableName.valueOf(snapshotDesc.getTable());
127    final TableDescriptor htd = createTableDescriptor(clonedTableName, CF);
128
129    long procId = ProcedureTestingUtility.submitAndWait(
130      procExec, new CloneSnapshotProcedure(procExec.getEnvironment(), htd, snapshotDesc));
131    Procedure<?> result = procExec.getResult(procId);
132    assertTrue(result.isFailed());
133    LOG.debug("Clone snapshot failed with exception: " + result.getException());
134    assertTrue(
135      ProcedureTestingUtility.getExceptionCause(result) instanceof TableExistsException);
136  }
137
138  @Test
139  public void testRecoveryAndDoubleExecution() throws Exception {
140    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
141    final TableName clonedTableName = TableName.valueOf("testRecoveryAndDoubleExecution");
142    final TableDescriptor htd = createTableDescriptor(clonedTableName, CF);
143
144    // take the snapshot
145    SnapshotProtos.SnapshotDescription snapshotDesc = getSnapshot();
146
147    // Here if you enable this then we will enter an infinite loop, as we will fail either after
148    // TRSP.openRegion or after OpenRegionProcedure.execute, so we can never finish the TRSP...
149    ProcedureTestingUtility.setKillIfHasParent(procExec, false);
150    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
151
152    // Start the Clone snapshot procedure && kill the executor
153    long procId = procExec.submitProcedure(
154      new CloneSnapshotProcedure(procExec.getEnvironment(), htd, snapshotDesc));
155
156    // Restart the executor and execute the step twice
157    MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId);
158
159    MasterProcedureTestingUtility.validateTableIsEnabled(
160      UTIL.getHBaseCluster().getMaster(),
161      clonedTableName);
162  }
163
164  @Test
165  public void testRollbackAndDoubleExecution() throws Exception {
166    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
167    final TableName clonedTableName = TableName.valueOf("testRollbackAndDoubleExecution");
168    final TableDescriptor htd = createTableDescriptor(clonedTableName, CF);
169
170    // take the snapshot
171    SnapshotProtos.SnapshotDescription snapshotDesc = getSnapshot();
172
173    ProcedureTestingUtility.waitNoProcedureRunning(procExec);
174    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
175
176    // Start the Clone snapshot procedure && kill the executor
177    long procId = procExec.submitProcedure(
178      new CloneSnapshotProcedure(procExec.getEnvironment(), htd, snapshotDesc));
179
180    int lastStep = 2; // failing before CLONE_SNAPSHOT_WRITE_FS_LAYOUT
181    MasterProcedureTestingUtility.testRollbackAndDoubleExecution(procExec, procId, lastStep);
182
183    MasterProcedureTestingUtility.validateTableDeletion(
184      UTIL.getHBaseCluster().getMaster(), clonedTableName);
185  }
186}