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