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