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;
019
020import java.io.IOException;
021import java.io.UncheckedIOException;
022import java.util.concurrent.CountDownLatch;
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.hbase.HBaseTestingUtil;
025import org.apache.hadoop.hbase.HConstants;
026import org.apache.hadoop.hbase.PleaseHoldException;
027import org.apache.hadoop.hbase.StartTestingClusterOption;
028import org.apache.hadoop.hbase.TableName;
029import org.apache.hadoop.hbase.client.Put;
030import org.apache.hadoop.hbase.client.RegionInfo;
031import org.apache.hadoop.hbase.client.Table;
032import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
033import org.apache.hadoop.hbase.master.region.MasterRegion;
034import org.apache.hadoop.hbase.regionserver.HRegionServer;
035import org.apache.hadoop.hbase.testclassification.MasterTests;
036import org.apache.hadoop.hbase.testclassification.MediumTests;
037import org.apache.hadoop.hbase.util.Bytes;
038import org.junit.jupiter.api.AfterAll;
039import org.junit.jupiter.api.BeforeAll;
040import org.junit.jupiter.api.Tag;
041import org.junit.jupiter.api.Test;
042
043import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.RegionStateTransition.TransitionCode;
044import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.ReportRegionStateTransitionRequest;
045import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.ReportRegionStateTransitionResponse;
046
047@Tag(MasterTests.TAG)
048@Tag(MediumTests.TAG)
049public class TestCloseAnOpeningRegion {
050
051  private static final HBaseTestingUtil UTIL = new HBaseTestingUtil();
052
053  private static TableName TABLE_NAME = TableName.valueOf("race");
054
055  private static byte[] CF = Bytes.toBytes("cf");
056
057  private static volatile CountDownLatch ARRIVE;
058
059  private static volatile CountDownLatch RESUME;
060
061  public static final class MockHMaster extends HMaster {
062
063    public MockHMaster(Configuration conf) throws IOException {
064      super(conf);
065    }
066
067    @Override
068    protected AssignmentManager createAssignmentManager(MasterServices master,
069      MasterRegion masterRegion) {
070      return new AssignmentManager(master, masterRegion) {
071
072        @Override
073        public ReportRegionStateTransitionResponse reportRegionStateTransition(
074          ReportRegionStateTransitionRequest req) throws PleaseHoldException {
075          ReportRegionStateTransitionResponse resp = super.reportRegionStateTransition(req);
076          TransitionCode code = req.getTransition(0).getTransitionCode();
077          if (code == TransitionCode.OPENED && ARRIVE != null) {
078            ARRIVE.countDown();
079            try {
080              RESUME.await();
081            } catch (InterruptedException e) {
082              throw new RuntimeException(e);
083            }
084          }
085          return resp;
086        }
087      };
088    }
089  }
090
091  @BeforeAll
092  public static void setUp() throws Exception {
093    UTIL.getConfiguration().setInt(HConstants.HBASE_RPC_SHORTOPERATION_TIMEOUT_KEY, 60000);
094    UTIL.startMiniCluster(StartTestingClusterOption.builder().numRegionServers(2)
095      .masterClass(MockHMaster.class).build());
096    UTIL.createTable(TABLE_NAME, CF);
097    UTIL.getAdmin().balancerSwitch(false, true);
098  }
099
100  @AfterAll
101  public static void tearDown() throws Exception {
102    UTIL.shutdownMiniCluster();
103  }
104
105  @Test
106  public void test() throws IOException, InterruptedException {
107    ARRIVE = new CountDownLatch(1);
108    RESUME = new CountDownLatch(1);
109    RegionInfo region = UTIL.getAdmin().getRegions(TABLE_NAME).get(0);
110    HRegionServer src = UTIL.getRSForFirstRegionInTable(TABLE_NAME);
111    HRegionServer dst = UTIL.getOtherRegionServer(src);
112    Thread move0 = new Thread(() -> {
113      try {
114        UTIL.getAdmin().move(region.getEncodedNameAsBytes(), dst.getServerName());
115      } catch (IOException e) {
116        throw new UncheckedIOException(e);
117      }
118    });
119    move0.start();
120    ARRIVE.await();
121    Thread move1 = new Thread(() -> {
122      try {
123        UTIL.getAdmin().move(region.getEncodedNameAsBytes(), src.getServerName());
124      } catch (IOException e) {
125        throw new UncheckedIOException(e);
126      }
127    });
128    move1.start();
129    // No simple way to determine when it is safe to go on and produce the race so let's sleep for a
130    // well...
131    Thread.sleep(10000);
132    RESUME.countDown();
133    move0.join();
134    move1.join();
135    try (Table table = UTIL.getConnection().getTable(TABLE_NAME)) {
136      // make sure that we can write to the table, which means the region is online
137      table.put(new Put(Bytes.toBytes(0)).addColumn(CF, Bytes.toBytes("cq"), Bytes.toBytes(0)));
138    }
139  }
140}