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