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.HBaseTestingUtil;
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 HBaseTestingUtil UTIL = new HBaseTestingUtil();
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    // Start the Modify procedure && kill the executor
244    long procId = procExec.submitProcedure(new ModifyNamespaceProcedure(procExec.getEnvironment(),
245      NamespaceDescriptor.create(nsd).addConfiguration(nsKey, nsValue).build()));
246
247    int lastStep = 2; // failing before MODIFY_NAMESPACE_UPDATE_NS_TABLE
248    MasterProcedureTestingUtility.testRollbackAndDoubleExecution(procExec, procId, lastStep);
249
250    // Validate
251    NamespaceDescriptor currentNsDescriptor =
252        UTIL.getAdmin().getNamespaceDescriptor(nsd.getName());
253    assertNull(currentNsDescriptor.getConfigurationValue(nsKey));
254  }
255
256  private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
257    return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
258  }
259
260  private void createNamespaceForTesting(NamespaceDescriptor nsDescriptor) throws Exception {
261    final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
262
263    long procId = procExec.submitProcedure(
264      new CreateNamespaceProcedure(procExec.getEnvironment(), nsDescriptor));
265    // Wait the completion
266    ProcedureTestingUtility.waitProcedure(procExec, procId);
267    ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
268  }
269}