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.assertNull; 022import static org.junit.Assert.assertTrue; 023 024import org.apache.hadoop.conf.Configuration; 025import org.apache.hadoop.hbase.HBaseClassTestRule; 026import org.apache.hadoop.hbase.HBaseTestingUtility; 027import org.apache.hadoop.hbase.NamespaceDescriptor; 028import org.apache.hadoop.hbase.NamespaceNotFoundException; 029import org.apache.hadoop.hbase.client.TableDescriptor; 030import org.apache.hadoop.hbase.constraint.ConstraintException; 031import org.apache.hadoop.hbase.procedure2.Procedure; 032import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 033import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility; 034import org.apache.hadoop.hbase.testclassification.MasterTests; 035import org.apache.hadoop.hbase.testclassification.MediumTests; 036import org.junit.After; 037import org.junit.AfterClass; 038import org.junit.Before; 039import org.junit.BeforeClass; 040import org.junit.ClassRule; 041import org.junit.Test; 042import org.junit.experimental.categories.Category; 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045 046@Category({MasterTests.class, MediumTests.class}) 047public class TestModifyNamespaceProcedure { 048 049 @ClassRule 050 public static final HBaseClassTestRule CLASS_RULE = 051 HBaseClassTestRule.forClass(TestModifyNamespaceProcedure.class); 052 053 private static final Logger LOG = LoggerFactory.getLogger(TestModifyNamespaceProcedure.class); 054 055 protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 056 057 private static void setupConf(Configuration conf) { 058 conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1); 059 } 060 061 @BeforeClass 062 public static void setupCluster() throws Exception { 063 setupConf(UTIL.getConfiguration()); 064 UTIL.startMiniCluster(1); 065 } 066 067 @AfterClass 068 public static void cleanupTest() throws Exception { 069 try { 070 UTIL.shutdownMiniCluster(); 071 } catch (Exception e) { 072 LOG.warn("failure shutting down cluster", e); 073 } 074 } 075 076 @Before 077 public void setup() throws Exception { 078 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false); 079 } 080 081 @After 082 public void tearDown() throws Exception { 083 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false); 084 for (TableDescriptor htd: UTIL.getAdmin().listTableDescriptors()) { 085 LOG.info("Tear down, remove table=" + htd.getTableName()); 086 UTIL.deleteTable(htd.getTableName()); 087 } 088 } 089 090 091 @Test 092 public void testModifyNamespace() throws Exception { 093 final NamespaceDescriptor nsd = NamespaceDescriptor.create("testModifyNamespace").build(); 094 final String nsKey1 = "hbase.namespace.quota.maxregions"; 095 final String nsValue1before = "1111"; 096 final String nsValue1after = "9999"; 097 final String nsKey2 = "hbase.namespace.quota.maxtables"; 098 final String nsValue2 = "10"; 099 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 100 101 nsd.setConfiguration(nsKey1, nsValue1before); 102 createNamespaceForTesting(nsd); 103 104 // Before modify 105 NamespaceDescriptor currentNsDescriptor = 106 UTIL.getAdmin().getNamespaceDescriptor(nsd.getName()); 107 assertEquals(nsValue1before, currentNsDescriptor.getConfigurationValue(nsKey1)); 108 assertNull(currentNsDescriptor.getConfigurationValue(nsKey2)); 109 110 // Update 111 nsd.setConfiguration(nsKey1, nsValue1after); 112 nsd.setConfiguration(nsKey2, nsValue2); 113 114 long procId1 = procExec.submitProcedure( 115 new ModifyNamespaceProcedure(procExec.getEnvironment(), nsd)); 116 // Wait the completion 117 ProcedureTestingUtility.waitProcedure(procExec, procId1); 118 ProcedureTestingUtility.assertProcNotFailed(procExec, procId1); 119 120 // Verify the namespace is updated. 121 currentNsDescriptor = 122 UTIL.getAdmin().getNamespaceDescriptor(nsd.getName()); 123 assertEquals(nsValue1after, nsd.getConfigurationValue(nsKey1)); 124 assertEquals(nsValue2, currentNsDescriptor.getConfigurationValue(nsKey2)); 125 } 126 127 @Test 128 public void testModifyNonExistNamespace() throws Exception { 129 final String namespaceName = "testModifyNonExistNamespace"; 130 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 131 132 try { 133 NamespaceDescriptor nsDescriptor = UTIL.getAdmin().getNamespaceDescriptor(namespaceName); 134 assertNull(nsDescriptor); 135 } catch (NamespaceNotFoundException nsnfe) { 136 // Expected 137 LOG.debug("The namespace " + namespaceName + " does not exist. This is expected."); 138 } 139 140 final NamespaceDescriptor nsd = NamespaceDescriptor.create(namespaceName).build(); 141 142 long procId = procExec.submitProcedure( 143 new ModifyNamespaceProcedure(procExec.getEnvironment(), nsd)); 144 // Wait the completion 145 ProcedureTestingUtility.waitProcedure(procExec, procId); 146 147 // Expect fail with NamespaceNotFoundException 148 Procedure<?> result = procExec.getResult(procId); 149 assertTrue(result.isFailed()); 150 LOG.debug("modify namespace failed with exception: " + result.getException()); 151 assertTrue( 152 ProcedureTestingUtility.getExceptionCause(result) instanceof NamespaceNotFoundException); 153 } 154 155 @Test 156 public void testModifyNamespaceWithInvalidRegionCount() throws Exception { 157 final NamespaceDescriptor nsd = 158 NamespaceDescriptor.create("testModifyNamespaceWithInvalidRegionCount").build(); 159 final String nsKey = "hbase.namespace.quota.maxregions"; 160 final String nsValue = "-1"; 161 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 162 163 createNamespaceForTesting(nsd); 164 165 // Modify 166 nsd.setConfiguration(nsKey, nsValue); 167 168 long procId = procExec.submitProcedure( 169 new ModifyNamespaceProcedure(procExec.getEnvironment(), nsd)); 170 // Wait the completion 171 ProcedureTestingUtility.waitProcedure(procExec, procId); 172 Procedure<?> result = procExec.getResult(procId); 173 assertTrue(result.isFailed()); 174 LOG.debug("Modify namespace failed with exception: " + result.getException()); 175 assertTrue(ProcedureTestingUtility.getExceptionCause(result) instanceof ConstraintException); 176 } 177 178 @Test 179 public void testModifyNamespaceWithInvalidTableCount() throws Exception { 180 final NamespaceDescriptor nsd = 181 NamespaceDescriptor.create("testModifyNamespaceWithInvalidTableCount").build(); 182 final String nsKey = "hbase.namespace.quota.maxtables"; 183 final String nsValue = "-1"; 184 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 185 186 createNamespaceForTesting(nsd); 187 188 // Modify 189 nsd.setConfiguration(nsKey, nsValue); 190 191 long procId = procExec.submitProcedure( 192 new ModifyNamespaceProcedure(procExec.getEnvironment(), nsd)); 193 // Wait the completion 194 ProcedureTestingUtility.waitProcedure(procExec, procId); 195 Procedure<?> result = procExec.getResult(procId); 196 assertTrue(result.isFailed()); 197 LOG.debug("Modify namespace failed with exception: " + result.getException()); 198 assertTrue(ProcedureTestingUtility.getExceptionCause(result) instanceof ConstraintException); 199 } 200 201 @Test 202 public void testRecoveryAndDoubleExecution() throws Exception { 203 final NamespaceDescriptor nsd = 204 NamespaceDescriptor.create("testRecoveryAndDoubleExecution").build(); 205 final String nsKey = "foo"; 206 final String nsValue = "bar"; 207 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 208 209 createNamespaceForTesting(nsd); 210 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 211 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 212 213 // Modify 214 nsd.setConfiguration(nsKey, nsValue); 215 216 // Start the Modify procedure && kill the executor 217 long procId = procExec.submitProcedure( 218 new ModifyNamespaceProcedure(procExec.getEnvironment(), nsd)); 219 220 // Restart the executor and execute the step twice 221 MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId); 222 223 ProcedureTestingUtility.assertProcNotFailed(procExec, procId); 224 // Validate 225 NamespaceDescriptor currentNsDescriptor = 226 UTIL.getAdmin().getNamespaceDescriptor(nsd.getName()); 227 assertEquals(nsValue, currentNsDescriptor.getConfigurationValue(nsKey)); 228 } 229 230 @Test 231 public void testRollbackAndDoubleExecution() throws Exception { 232 final NamespaceDescriptor nsd = 233 NamespaceDescriptor.create("testRollbackAndDoubleExecution").build(); 234 final String nsKey = "foo"; 235 final String nsValue = "bar"; 236 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 237 238 createNamespaceForTesting(nsd); 239 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 240 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 241 242 // Modify 243 nsd.setConfiguration(nsKey, nsValue); 244 245 // Start the Modify procedure && kill the executor 246 long procId = procExec.submitProcedure( 247 new ModifyNamespaceProcedure(procExec.getEnvironment(), nsd)); 248 249 int lastStep = 2; // failing before MODIFY_NAMESPACE_UPDATE_NS_TABLE 250 MasterProcedureTestingUtility.testRollbackAndDoubleExecution(procExec, procId, lastStep); 251 252 // Validate 253 NamespaceDescriptor currentNsDescriptor = 254 UTIL.getAdmin().getNamespaceDescriptor(nsd.getName()); 255 assertNull(currentNsDescriptor.getConfigurationValue(nsKey)); 256 } 257 258 private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() { 259 return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor(); 260 } 261 262 private void createNamespaceForTesting(NamespaceDescriptor nsDescriptor) throws Exception { 263 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 264 265 long procId = procExec.submitProcedure( 266 new CreateNamespaceProcedure(procExec.getEnvironment(), nsDescriptor)); 267 // Wait the completion 268 ProcedureTestingUtility.waitProcedure(procExec, procId); 269 ProcedureTestingUtility.assertProcNotFailed(procExec, procId); 270 } 271}