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.procedure; 019 020import static org.apache.hadoop.hbase.coprocessor.CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY; 021import static org.junit.Assert.fail; 022 023import java.io.IOException; 024import java.util.List; 025import java.util.Optional; 026import org.apache.hadoop.conf.Configuration; 027import org.apache.hadoop.hbase.HBaseClassTestRule; 028import org.apache.hadoop.hbase.HBaseTestingUtility; 029import org.apache.hadoop.hbase.TableName; 030import org.apache.hadoop.hbase.client.RegionInfo; 031import org.apache.hadoop.hbase.client.TableDescriptor; 032import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 033import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 034import org.apache.hadoop.hbase.coprocessor.MasterObserver; 035import org.apache.hadoop.hbase.coprocessor.ObserverContext; 036import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; 037import org.apache.hadoop.hbase.procedure2.Procedure; 038import org.apache.hadoop.hbase.security.AccessDeniedException; 039import org.apache.hadoop.hbase.testclassification.LargeTests; 040import org.apache.hadoop.hbase.util.Bytes; 041import org.junit.After; 042import org.junit.BeforeClass; 043import org.junit.ClassRule; 044import org.junit.Test; 045import org.junit.experimental.categories.Category; 046 047import org.slf4j.Logger; 048import org.slf4j.LoggerFactory; 049 050import org.apache.hadoop.hbase.shaded.protobuf.generated.ProcedureProtos; 051 052/** 053 * Check if CompletedProcedureCleaner cleans up failed nonce procedures. 054 */ 055@Category(LargeTests.class) 056public class TestFailedProcCleanup { 057 058 @ClassRule 059 public static final HBaseClassTestRule CLASS_RULE = 060 HBaseClassTestRule.forClass(TestFailedProcCleanup.class); 061 062 private static final Logger LOG = LoggerFactory.getLogger(TestFailedProcCleanup.class); 063 064 protected static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 065 private static Configuration conf; 066 private static final TableName TABLE = TableName.valueOf("test"); 067 private static final byte[] FAMILY = Bytes.toBytesBinary("f"); 068 private static final int evictionDelay = 10 * 1000; 069 070 @BeforeClass 071 public static void setUpBeforeClass() { 072 conf = TEST_UTIL.getConfiguration(); 073 conf.setInt("hbase.procedure.cleaner.evict.ttl", evictionDelay); 074 conf.setInt("hbase.procedure.cleaner.evict.batch.size", 1); 075 } 076 077 @After 078 public void tearDown() throws Exception { 079 TEST_UTIL.shutdownMiniCluster(); 080 } 081 082 @Test 083 public void testFailCreateTable() throws Exception { 084 conf.set(MASTER_COPROCESSOR_CONF_KEY, CreateFailObserver.class.getName()); 085 TEST_UTIL.startMiniCluster(3); 086 try { 087 TEST_UTIL.createTable(TABLE, FAMILY); 088 } catch (AccessDeniedException e) { 089 LOG.debug("Ignoring exception: ", e); 090 Thread.sleep(evictionDelay * 3); 091 } 092 List<Procedure<MasterProcedureEnv>> procedureInfos = 093 TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor().getProcedures(); 094 for (Procedure procedureInfo : procedureInfos) { 095 if (procedureInfo.getProcName().equals("CreateTableProcedure") 096 && procedureInfo.getState() == ProcedureProtos.ProcedureState.ROLLEDBACK) { 097 fail("Found procedure " + procedureInfo + " that hasn't been cleaned up"); 098 } 099 } 100 } 101 102 @Test 103 public void testFailCreateTableAction() throws Exception { 104 conf.set(MASTER_COPROCESSOR_CONF_KEY, CreateFailObserverHandler.class.getName()); 105 TEST_UTIL.startMiniCluster(3); 106 try { 107 TEST_UTIL.createTable(TABLE, FAMILY); 108 fail("Table shouldn't be created"); 109 } catch (AccessDeniedException e) { 110 LOG.debug("Ignoring exception: ", e); 111 Thread.sleep(evictionDelay * 3); 112 } 113 List<Procedure<MasterProcedureEnv>> procedureInfos = 114 TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor().getProcedures(); 115 for (Procedure procedureInfo : procedureInfos) { 116 if (procedureInfo.getProcName().equals("CreateTableProcedure") 117 && procedureInfo.getState() == ProcedureProtos.ProcedureState.ROLLEDBACK) { 118 fail("Found procedure " + procedureInfo + " that hasn't been cleaned up"); 119 } 120 } 121 } 122 123 public static class CreateFailObserver implements MasterCoprocessor, MasterObserver { 124 125 @Override 126 public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> env, 127 TableDescriptor desc, RegionInfo[] regions) throws IOException { 128 129 if (desc.getTableName().equals(TABLE)) { 130 throw new AccessDeniedException("Don't allow creation of table"); 131 } 132 } 133 134 @Override 135 public Optional<MasterObserver> getMasterObserver() { 136 return Optional.of(this); 137 } 138 } 139 140 public static class CreateFailObserverHandler implements MasterCoprocessor, MasterObserver { 141 142 @Override 143 public void preCreateTableAction( 144 final ObserverContext<MasterCoprocessorEnvironment> ctx, final TableDescriptor desc, 145 final RegionInfo[] regions) throws IOException { 146 147 if (desc.getTableName().equals(TABLE)) { 148 throw new AccessDeniedException("Don't allow creation of table"); 149 } 150 } 151 152 @Override 153 public Optional<MasterObserver> getMasterObserver() { 154 return Optional.of(this); 155 } 156 } 157}