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.client;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertTrue;
022
023import java.util.ArrayList;
024import java.util.EnumSet;
025import java.util.HashMap;
026import java.util.List;
027import java.util.function.Supplier;
028import java.util.stream.Collectors;
029import org.apache.hadoop.hbase.ClusterMetrics.Option;
030import org.apache.hadoop.hbase.HBaseParameterizedTestTemplate;
031import org.apache.hadoop.hbase.ServerName;
032import org.apache.hadoop.hbase.testclassification.ClientTests;
033import org.apache.hadoop.hbase.testclassification.MediumTests;
034import org.junit.jupiter.api.AfterAll;
035import org.junit.jupiter.api.BeforeAll;
036import org.junit.jupiter.api.Tag;
037import org.junit.jupiter.api.TestTemplate;
038
039@Tag(ClientTests.TAG)
040@Tag(MediumTests.TAG)
041@HBaseParameterizedTestTemplate(name = "{index}: policy = {0}")
042public class TestAsyncDecommissionAdminApi extends TestAsyncAdminBase {
043
044  public TestAsyncDecommissionAdminApi(Supplier<AsyncAdmin> admin) {
045    super(admin);
046  }
047
048  @BeforeAll
049  public static void setUpBeforeClass() throws Exception {
050    TestAsyncAdminBase.setUpBeforeClass();
051  }
052
053  @AfterAll
054  public static void tearDownAfterClass() throws Exception {
055    TestAsyncAdminBase.tearDownAfterClass();
056  }
057
058  @TestTemplate
059  public void testAsyncDecommissionRegionServers() throws Exception {
060    admin.balancerSwitch(false, true);
061    List<ServerName> decommissionedRegionServers = admin.listDecommissionedRegionServers().get();
062    assertTrue(decommissionedRegionServers.isEmpty());
063
064    TEST_UTIL.createMultiRegionTable(tableName, FAMILY, 4);
065
066    ArrayList<ServerName> clusterRegionServers = new ArrayList<>(admin
067      .getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)).get().getLiveServerMetrics().keySet());
068
069    assertEquals(TEST_UTIL.getHBaseCluster().getLiveRegionServerThreads().size(),
070      clusterRegionServers.size());
071
072    HashMap<ServerName, List<RegionInfo>> serversToDecommssion = new HashMap<>();
073    // Get a server that has regions. We will decommission one of the servers,
074    // leaving one online.
075    int i;
076    for (i = 0; i < clusterRegionServers.size(); i++) {
077      List<RegionInfo> regionsOnServer = admin.getRegions(clusterRegionServers.get(i)).get();
078      if (regionsOnServer.size() > 0) {
079        serversToDecommssion.put(clusterRegionServers.get(i), regionsOnServer);
080        break;
081      }
082    }
083
084    clusterRegionServers.remove(i);
085    ServerName remainingServer = clusterRegionServers.get(0);
086
087    // Decommission
088    admin.decommissionRegionServers(new ArrayList<ServerName>(serversToDecommssion.keySet()), true)
089      .get();
090    assertEquals(1, admin.listDecommissionedRegionServers().get().size());
091
092    // Verify the regions have been off the decommissioned servers, all on the remaining server.
093    for (ServerName server : serversToDecommssion.keySet()) {
094      for (RegionInfo region : serversToDecommssion.get(server)) {
095        TEST_UTIL.assertRegionOnServer(region, remainingServer, 10000);
096      }
097    }
098
099    // Maybe the TRSP is still not finished at master side, since the reportRegionTransition just
100    // updates the procedure store, and we still need to wake up the procedure and execute it in the
101    // procedure executor, which is asynchronous
102    TEST_UTIL.waitUntilNoRegionsInTransition(10000);
103
104    // Recommission and load regions
105    for (ServerName server : serversToDecommssion.keySet()) {
106      List<byte[]> encodedRegionNames = serversToDecommssion.get(server).stream()
107        .map(region -> region.getEncodedNameAsBytes()).collect(Collectors.toList());
108      admin.recommissionRegionServer(server, encodedRegionNames).get();
109    }
110    assertTrue(admin.listDecommissionedRegionServers().get().isEmpty());
111    // Verify the regions have been moved to the recommissioned servers
112    for (ServerName server : serversToDecommssion.keySet()) {
113      for (RegionInfo region : serversToDecommssion.get(server)) {
114        TEST_UTIL.assertRegionOnServer(region, server, 10000);
115      }
116    }
117  }
118}