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.assertTrue;
021
022import java.io.IOException;
023import org.apache.hadoop.hbase.HBaseClassTestRule;
024import org.apache.hadoop.hbase.HBaseTestingUtil;
025import org.apache.hadoop.hbase.TableName;
026import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
027import org.apache.hadoop.hbase.client.RegionInfo;
028import org.apache.hadoop.hbase.client.RegionInfoBuilder;
029import org.apache.hadoop.hbase.client.TableDescriptor;
030import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
031import org.apache.hadoop.hbase.procedure2.Procedure;
032import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
033import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer;
034import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException;
035import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
036import org.apache.hadoop.hbase.procedure2.ProcedureYieldException;
037import org.apache.hadoop.hbase.testclassification.MasterTests;
038import org.apache.hadoop.hbase.testclassification.MediumTests;
039import org.junit.AfterClass;
040import org.junit.BeforeClass;
041import org.junit.ClassRule;
042import org.junit.Test;
043import org.junit.experimental.categories.Category;
044
045/**
046 * Testcase for HBASE-28876
047 */
048@Category({ MasterTests.class, MediumTests.class })
049public class TestTableProcedureWaitingQueueCleanup {
050
051  @ClassRule
052  public static final HBaseClassTestRule CLASS_RULE =
053    HBaseClassTestRule.forClass(TestTableProcedureWaitingQueueCleanup.class);
054
055  private static final HBaseTestingUtil UTIL = new HBaseTestingUtil();
056
057  private static TableDescriptor TD = TableDescriptorBuilder.newBuilder(TableName.valueOf("test"))
058    .setColumnFamily(ColumnFamilyDescriptorBuilder.of("cf")).build();
059
060  // In current HBase code base, we do not use table procedure as sub procedure, so here we need to
061  // introduce one for testing
062  public static class NonTableProcedure extends Procedure<MasterProcedureEnv>
063    implements PeerProcedureInterface {
064
065    private boolean created = false;
066
067    @Override
068    protected Procedure<MasterProcedureEnv>[] execute(MasterProcedureEnv env)
069      throws ProcedureYieldException, ProcedureSuspendedException, InterruptedException {
070      if (created) {
071        return null;
072      }
073      created = true;
074      return new Procedure[] { new CreateTableProcedure(env, TD,
075        new RegionInfo[] { RegionInfoBuilder.newBuilder(TD.getTableName()).build() }) };
076    }
077
078    @Override
079    protected void rollback(MasterProcedureEnv env) throws IOException, InterruptedException {
080      throw new UnsupportedOperationException();
081    }
082
083    @Override
084    protected boolean abort(MasterProcedureEnv env) {
085      return false;
086    }
087
088    @Override
089    protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException {
090    }
091
092    @Override
093    protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException {
094    }
095
096    @Override
097    public String getPeerId() {
098      return "peer";
099    }
100
101    @Override
102    public PeerOperationType getPeerOperationType() {
103      return PeerOperationType.ENABLE;
104    }
105  }
106
107  @BeforeClass
108  public static void setUp() throws Exception {
109    UTIL.startMiniCluster();
110  }
111
112  @AfterClass
113  public static void tearDown() throws Exception {
114    UTIL.shutdownMiniCluster();
115  }
116
117  // the root procedure will lock meta but we will schedule a table procedure for other table
118  public static class MetaTableProcedure extends Procedure<MasterProcedureEnv>
119    implements TableProcedureInterface {
120
121    private boolean created = false;
122
123    @Override
124    public TableName getTableName() {
125      return TableName.META_TABLE_NAME;
126    }
127
128    @Override
129    public TableOperationType getTableOperationType() {
130      return TableOperationType.EDIT;
131    }
132
133    @Override
134    protected Procedure<MasterProcedureEnv>[] execute(MasterProcedureEnv env)
135      throws ProcedureYieldException, ProcedureSuspendedException, InterruptedException {
136      if (created) {
137        return null;
138      }
139      created = true;
140      return new Procedure[] { new CreateTableProcedure(env, TD,
141        new RegionInfo[] { RegionInfoBuilder.newBuilder(TD.getTableName()).build() }) };
142    }
143
144    @Override
145    protected void rollback(MasterProcedureEnv env) throws IOException, InterruptedException {
146      throw new UnsupportedOperationException();
147    }
148
149    @Override
150    protected boolean abort(MasterProcedureEnv env) {
151      return false;
152    }
153
154    @Override
155    protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException {
156    }
157
158    @Override
159    protected void deserializeStateData(ProcedureStateSerializer serializer) throws IOException {
160    }
161  }
162
163  private void testCreateDelete(Procedure<MasterProcedureEnv> proc) throws Exception {
164    ProcedureExecutor<MasterProcedureEnv> procExec =
165      UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor();
166    ProcedureTestingUtility.submitAndWait(procExec, proc);
167    assertTrue(UTIL.getAdmin().tableExists(TD.getTableName()));
168
169    // Without the fix in HBASE-28876, we will hang there forever, as we do not clean up the
170    // TableProcedureWaitingQueue
171    UTIL.getAdmin().disableTable(TD.getTableName());
172    UTIL.getAdmin().deleteTable(TD.getTableName());
173  }
174
175  @Test
176  public void testNonTableProcedure() throws Exception {
177    testCreateDelete(new NonTableProcedure());
178  }
179
180  @Test
181  public void testNotSameTable() throws Exception {
182    testCreateDelete(new MetaTableProcedure());
183  }
184}