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.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNotEquals; 023import static org.junit.Assert.assertTrue; 024 025import java.io.IOException; 026import java.util.List; 027import org.apache.hadoop.hbase.HBaseClassTestRule; 028import org.apache.hadoop.hbase.TableName; 029import org.apache.hadoop.hbase.TableNotDisabledException; 030import org.apache.hadoop.hbase.TableNotFoundException; 031import org.apache.hadoop.hbase.client.Admin; 032import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 033import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 034import org.apache.hadoop.hbase.client.SnapshotDescription; 035import org.apache.hadoop.hbase.client.TableDescriptor; 036import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 037import org.apache.hadoop.hbase.procedure2.Procedure; 038import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 039import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility; 040import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils; 041import org.apache.hadoop.hbase.testclassification.LargeTests; 042import org.apache.hadoop.hbase.testclassification.MasterTests; 043import org.apache.hadoop.hbase.util.Bytes; 044import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 045import org.junit.After; 046import org.junit.Before; 047import org.junit.ClassRule; 048import org.junit.Rule; 049import org.junit.Test; 050import org.junit.experimental.categories.Category; 051import org.junit.rules.TestName; 052import org.slf4j.Logger; 053import org.slf4j.LoggerFactory; 054 055import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 056import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos; 057 058@Category({ MasterTests.class, LargeTests.class }) 059public class TestRestoreSnapshotProcedure extends TestTableDDLProcedureBase { 060 061 @ClassRule 062 public static final HBaseClassTestRule CLASS_RULE = 063 HBaseClassTestRule.forClass(TestRestoreSnapshotProcedure.class); 064 065 private static final Logger LOG = LoggerFactory.getLogger(TestRestoreSnapshotProcedure.class); 066 067 protected final TableName snapshotTableName = TableName.valueOf("testRestoreSnapshot"); 068 protected final byte[] CF1 = Bytes.toBytes("cf1"); 069 protected final byte[] CF2 = Bytes.toBytes("cf2"); 070 protected final byte[] CF3 = Bytes.toBytes("cf3"); 071 protected final byte[] CF4 = Bytes.toBytes("cf4"); 072 protected final int rowCountCF1 = 10; 073 protected final int rowCountCF2 = 40; 074 protected final int rowCountCF3 = 40; 075 protected final int rowCountCF4 = 40; 076 protected final int rowCountCF1addition = 10; 077 078 private SnapshotProtos.SnapshotDescription snapshot = null; 079 private TableDescriptor snapshotHTD = null; 080 081 @Rule 082 public TestName name = new TestName(); 083 084 @Before 085 @Override 086 public void setup() throws Exception { 087 super.setup(); 088 setupSnapshotAndUpdateTable(); 089 } 090 091 @After 092 @Override 093 public void tearDown() throws Exception { 094 super.tearDown(); 095 SnapshotTestingUtils.deleteAllSnapshots(UTIL.getAdmin()); 096 SnapshotTestingUtils.deleteArchiveDirectory(UTIL); 097 } 098 099 private int getNumReplicas() { 100 return 1; 101 } 102 103 private void setupSnapshotAndUpdateTable() throws Exception { 104 long tid = EnvironmentEdgeManager.currentTime(); 105 final String snapshotName = "snapshot-" + tid; 106 Admin admin = UTIL.getAdmin(); 107 // create Table 108 SnapshotTestingUtils.createTable(UTIL, snapshotTableName, getNumReplicas(), CF1, CF2); 109 // Load data 110 SnapshotTestingUtils.loadData(UTIL, snapshotTableName, rowCountCF1, CF1); 111 SnapshotTestingUtils.loadData(UTIL, snapshotTableName, rowCountCF2, CF2); 112 SnapshotTestingUtils.verifyRowCount(UTIL, snapshotTableName, rowCountCF1 + rowCountCF2); 113 114 snapshotHTD = admin.getDescriptor(snapshotTableName); 115 116 admin.disableTable(snapshotTableName); 117 // take a snapshot 118 admin.snapshot(snapshotName, snapshotTableName); 119 120 List<SnapshotDescription> snapshotList = admin.listSnapshots(); 121 snapshot = ProtobufUtil.createHBaseProtosSnapshotDesc(snapshotList.get(0)); 122 123 // modify the table 124 ColumnFamilyDescriptor columnFamilyDescriptor3 = ColumnFamilyDescriptorBuilder.of(CF3); 125 ColumnFamilyDescriptor columnFamilyDescriptor4 = ColumnFamilyDescriptorBuilder.of(CF4); 126 admin.addColumnFamily(snapshotTableName, columnFamilyDescriptor3); 127 admin.addColumnFamily(snapshotTableName, columnFamilyDescriptor4); 128 admin.deleteColumnFamily(snapshotTableName, CF2); 129 // enable table and insert data 130 admin.enableTable(snapshotTableName); 131 SnapshotTestingUtils.loadData(UTIL, snapshotTableName, rowCountCF3, CF3); 132 SnapshotTestingUtils.loadData(UTIL, snapshotTableName, rowCountCF4, CF4); 133 SnapshotTestingUtils.loadData(UTIL, snapshotTableName, rowCountCF1addition, CF1); 134 TableDescriptor currentHTD = admin.getDescriptor(snapshotTableName); 135 assertTrue(currentHTD.hasColumnFamily(CF1)); 136 assertFalse(currentHTD.hasColumnFamily(CF2)); 137 assertTrue(currentHTD.hasColumnFamily(CF3)); 138 assertTrue(currentHTD.hasColumnFamily(CF4)); 139 assertNotEquals(currentHTD.getColumnFamilyNames().size(), 140 snapshotHTD.getColumnFamilies().length); 141 SnapshotTestingUtils.verifyRowCount(UTIL, snapshotTableName, 142 rowCountCF1 + rowCountCF3 + rowCountCF4 + rowCountCF1addition); 143 admin.disableTable(snapshotTableName); 144 } 145 146 private static TableDescriptor createHTableDescriptor(final TableName tableName, 147 final byte[]... family) { 148 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName); 149 150 for (int i = 0; i < family.length; ++i) { 151 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family[i])); 152 } 153 return builder.build(); 154 } 155 156 @Test 157 public void testRestoreSnapshot() throws Exception { 158 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 159 160 long procId = ProcedureTestingUtility.submitAndWait(procExec, 161 new RestoreSnapshotProcedure(procExec.getEnvironment(), snapshotHTD, snapshot)); 162 ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId)); 163 164 validateSnapshotRestore(); 165 } 166 167 @Test 168 public void testRestoreSnapshotToDifferentTable() throws Exception { 169 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 170 final TableName restoredTableName = TableName.valueOf(name.getMethodName()); 171 final TableDescriptor tableDescriptor = createHTableDescriptor(restoredTableName, CF1, CF2); 172 173 long procId = ProcedureTestingUtility.submitAndWait(procExec, 174 new RestoreSnapshotProcedure(procExec.getEnvironment(), tableDescriptor, snapshot)); 175 Procedure<?> result = procExec.getResult(procId); 176 assertTrue(result.isFailed()); 177 LOG.debug("Restore snapshot failed with exception: " + result.getException()); 178 assertTrue(ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotFoundException); 179 } 180 181 @Test 182 public void testRestoreSnapshotToEnabledTable() throws Exception { 183 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 184 185 try { 186 UTIL.getAdmin().enableTable(snapshotTableName); 187 188 long procId = ProcedureTestingUtility.submitAndWait(procExec, 189 new RestoreSnapshotProcedure(procExec.getEnvironment(), snapshotHTD, snapshot)); 190 Procedure<?> result = procExec.getResult(procId); 191 assertTrue(result.isFailed()); 192 LOG.debug("Restore snapshot failed with exception: " + result.getException()); 193 assertTrue( 194 ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotDisabledException); 195 } finally { 196 UTIL.getAdmin().disableTable(snapshotTableName); 197 } 198 } 199 200 @Test 201 public void testRecoveryAndDoubleExecution() throws Exception { 202 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 203 204 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 205 206 // Start the Restore snapshot procedure && kill the executor 207 long procId = procExec.submitProcedure( 208 new RestoreSnapshotProcedure(procExec.getEnvironment(), snapshotHTD, snapshot)); 209 210 // Restart the executor and execute the step twice 211 MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId); 212 213 resetProcExecutorTestingKillFlag(); 214 validateSnapshotRestore(); 215 } 216 217 @Test 218 public void testRecoverWithRestoreAclFlag() throws Exception { 219 // This test is to solve the problems mentioned in HBASE-26462, 220 // this needs to simulate the case of RestoreSnapshotProcedure failure and recovery, 221 // and verify whether 'restoreAcl' flag can obtain the correct value. 222 223 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 224 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 225 226 // Start the Restore snapshot procedure (with restoreAcl 'true') && kill the executor 227 long procId = procExec.submitProcedure( 228 new RestoreSnapshotProcedure(procExec.getEnvironment(), snapshotHTD, snapshot, true)); 229 MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId); 230 231 RestoreSnapshotProcedure result = (RestoreSnapshotProcedure) procExec.getResult(procId); 232 // check whether the restoreAcl flag is true after deserialization from Pb. 233 assertEquals(true, result.getRestoreAcl()); 234 } 235 236 private void validateSnapshotRestore() throws IOException { 237 try { 238 UTIL.getAdmin().enableTable(snapshotTableName); 239 240 TableDescriptor currentHTD = UTIL.getAdmin().getDescriptor(snapshotTableName); 241 assertTrue(currentHTD.hasColumnFamily(CF1)); 242 assertTrue(currentHTD.hasColumnFamily(CF2)); 243 assertFalse(currentHTD.hasColumnFamily(CF3)); 244 assertFalse(currentHTD.hasColumnFamily(CF4)); 245 assertEquals(currentHTD.getColumnFamilyNames().size(), 246 snapshotHTD.getColumnFamilies().length); 247 SnapshotTestingUtils.verifyRowCount(UTIL, snapshotTableName, rowCountCF1 + rowCountCF2); 248 } finally { 249 UTIL.getAdmin().disableTable(snapshotTableName); 250 } 251 } 252}