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