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}