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.assignment;
019
020import static org.junit.Assert.assertFalse;
021import static org.junit.Assert.assertSame;
022import static org.junit.Assert.assertTrue;
023
024import java.io.IOException;
025import java.util.concurrent.CountDownLatch;
026import java.util.concurrent.ForkJoinPool;
027import java.util.concurrent.Future;
028import java.util.concurrent.atomic.AtomicBoolean;
029import org.apache.hadoop.conf.Configuration;
030import org.apache.hadoop.hbase.HBaseClassTestRule;
031import org.apache.hadoop.hbase.HBaseTestingUtility;
032import org.apache.hadoop.hbase.HConstants;
033import org.apache.hadoop.hbase.TableName;
034import org.apache.hadoop.hbase.regionserver.HRegionServer;
035import org.apache.hadoop.hbase.regionserver.RSRpcServices;
036import org.apache.hadoop.hbase.testclassification.MasterTests;
037import org.apache.hadoop.hbase.testclassification.MediumTests;
038import org.apache.hadoop.hbase.util.Bytes;
039import org.junit.AfterClass;
040import org.junit.BeforeClass;
041import org.junit.ClassRule;
042import org.junit.Test;
043import org.junit.experimental.categories.Category;
044
045import org.apache.hbase.thirdparty.com.google.protobuf.RpcController;
046import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException;
047
048import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.ExecuteProceduresRequest;
049import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.ExecuteProceduresResponse;
050
051/**
052 * UT for HBASE-25032.
053 */
054@Category({ MasterTests.class, MediumTests.class })
055public class TestAssignRegionToUninitializedRegionServer {
056
057  @ClassRule
058  public static final HBaseClassTestRule CLASS_RULE =
059    HBaseClassTestRule.forClass(TestAssignRegionToUninitializedRegionServer.class);
060
061  private static CountDownLatch ARRIVE;
062
063  private static CountDownLatch RESUME;
064
065  private static AtomicBoolean ASSIGN_CALLED = new AtomicBoolean(false);
066
067  public static final class RSRpcServicesForTest extends RSRpcServices {
068
069    public RSRpcServicesForTest(HRegionServer rs) throws IOException {
070      super(rs);
071    }
072
073    @Override
074    public ExecuteProceduresResponse executeProcedures(RpcController controller,
075      ExecuteProceduresRequest request) throws ServiceException {
076      if (request.getOpenRegionCount() > 0) {
077        ASSIGN_CALLED.set(true);
078      }
079      return super.executeProcedures(controller, request);
080    }
081  }
082
083  public static final class RegionServerForTest extends HRegionServer {
084
085    public RegionServerForTest(Configuration conf) throws IOException {
086      super(conf);
087    }
088
089    @Override
090    protected void tryRegionServerReport(long reportStartTime, long reportEndTime)
091      throws IOException {
092      if (ARRIVE != null) {
093        ARRIVE.countDown();
094        ARRIVE = null;
095        try {
096          RESUME.await();
097        } catch (InterruptedException e) {
098        }
099      }
100      super.tryRegionServerReport(reportStartTime, reportEndTime);
101    }
102
103    @Override
104    protected RSRpcServices createRpcServices() throws IOException {
105      return new RSRpcServicesForTest(this);
106    }
107  }
108
109  private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
110
111  private static TableName NAME = TableName.valueOf("test");
112
113  private static byte[] FAMILY = Bytes.toBytes("family");
114
115  @BeforeClass
116  public static void setUp() throws Exception {
117    UTIL.startMiniCluster(1);
118    UTIL.createTable(NAME, FAMILY);
119    UTIL.waitTableAvailable(NAME);
120  }
121
122  @AfterClass
123  public static void tearDown() throws IOException {
124    UTIL.shutdownMiniCluster();
125  }
126
127  @Test
128  public void testMove() throws Exception {
129    UTIL.getMiniHBaseCluster().getConfiguration().setClass(HConstants.REGION_SERVER_IMPL,
130      RegionServerForTest.class, HRegionServer.class);
131    CountDownLatch arrive = new CountDownLatch(1);
132    ARRIVE = arrive;
133    RESUME = new CountDownLatch(1);
134    // restart a new region server, and wait until it finish initialization and want to call
135    // regionServerReport, so it will load the peer state to peer cache.
136    Future<HRegionServer> regionServerFuture = ForkJoinPool.commonPool()
137      .submit(() -> UTIL.getMiniHBaseCluster().startRegionServer().getRegionServer());
138    ARRIVE.await();
139    // try move region to the new region server, it will fail, but we need to make sure that we do
140    // not try to assign it to the new server.
141    HRegionServer src = UTIL.getRSForFirstRegionInTable(NAME);
142    HRegionServer dst = UTIL.getOtherRegionServer(src);
143    try {
144      UTIL.getAdmin().move(UTIL.getAdmin().getRegions(NAME).get(0).getEncodedNameAsBytes(),
145        dst.getServerName());
146      // assert the region should still on the original region server, and we didn't call assign to
147      // the new server
148      assertSame(src, UTIL.getRSForFirstRegionInTable(NAME));
149      assertFalse(ASSIGN_CALLED.get());
150    } finally {
151      // let the region server go
152      RESUME.countDown();
153    }
154    // wait the new region server online
155    assertSame(dst, regionServerFuture.get());
156    // try move again
157    UTIL.getAdmin().move(UTIL.getAdmin().getRegions(NAME).get(0).getEncodedNameAsBytes(),
158      dst.getServerName());
159    // this time the region should be on the new region server
160    assertSame(dst, UTIL.getRSForFirstRegionInTable(NAME));
161    assertTrue(ASSIGN_CALLED.get());
162  }
163}