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.regionserver;
019
020import static org.junit.jupiter.api.Assertions.assertTrue;
021
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.Collection;
025import java.util.HashMap;
026import java.util.Map;
027import org.apache.hadoop.conf.Configuration;
028import org.apache.hadoop.hbase.HBaseTestingUtil;
029import org.apache.hadoop.hbase.HConstants;
030import org.apache.hadoop.hbase.ServerName;
031import org.apache.hadoop.hbase.TableName;
032import org.apache.hadoop.hbase.client.RegionInfo;
033import org.apache.hadoop.hbase.client.Table;
034import org.apache.hadoop.hbase.client.TableDescriptor;
035import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
036import org.apache.hadoop.hbase.testclassification.MediumTests;
037import org.apache.hadoop.hbase.testclassification.RegionServerTests;
038import org.apache.hadoop.hbase.util.Bytes;
039import org.apache.hadoop.hbase.util.RegionSplitter;
040import org.junit.jupiter.api.AfterAll;
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
047@Tag(RegionServerTests.TAG)
048@Tag(MediumTests.TAG)
049public class TestRegionReplicasAreDistributed {
050
051  private static final Logger LOG = LoggerFactory.getLogger(TestRegionReplicasAreDistributed.class);
052
053  private static final int NB_SERVERS = 3;
054  private static Table table;
055
056  private static final HBaseTestingUtil HTU = new HBaseTestingUtil();
057  private static final byte[] f = HConstants.CATALOG_FAMILY;
058  Map<ServerName, Collection<RegionInfo>> serverVsOnlineRegions;
059  Map<ServerName, Collection<RegionInfo>> serverVsOnlineRegions2;
060  Map<ServerName, Collection<RegionInfo>> serverVsOnlineRegions3;
061  Map<ServerName, Collection<RegionInfo>> serverVsOnlineRegions4;
062
063  @BeforeAll
064  public static void before() throws Exception {
065    HTU.getConfiguration().setInt("hbase.master.wait.on.regionservers.mintostart", 3);
066
067    HTU.startMiniCluster(NB_SERVERS);
068    Thread.sleep(3000);
069    final TableName tableName =
070      TableName.valueOf(TestRegionReplicasAreDistributed.class.getSimpleName());
071
072    // Create table then get the single region for our new table.
073    createTableDirectlyFromHTD(tableName);
074  }
075
076  private static void createTableDirectlyFromHTD(final TableName tableName) throws IOException {
077    TableDescriptor htd =
078      TableDescriptorBuilder.newBuilder(tableName).setRegionReplication(3).build();
079    // create a table with 3 replication
080
081    table = HTU.createTable(htd, new byte[][] { f }, getSplits(20),
082      new Configuration(HTU.getConfiguration()));
083  }
084
085  private static byte[][] getSplits(int numRegions) {
086    RegionSplitter.UniformSplit split = new RegionSplitter.UniformSplit();
087    split.setFirstRow(Bytes.toBytes(0L));
088    split.setLastRow(Bytes.toBytes(Long.MAX_VALUE));
089    return split.split(numRegions);
090  }
091
092  @AfterAll
093  public static void afterClass() throws Exception {
094    HRegionServer.TEST_SKIP_REPORTING_TRANSITION = false;
095    table.close();
096    HTU.shutdownMiniCluster();
097  }
098
099  private HRegionServer getRS() {
100    return HTU.getMiniHBaseCluster().getRegionServer(0);
101  }
102
103  private HRegionServer getSecondaryRS() {
104    return HTU.getMiniHBaseCluster().getRegionServer(1);
105  }
106
107  private HRegionServer getTertiaryRS() {
108    return HTU.getMiniHBaseCluster().getRegionServer(2);
109  }
110
111  @Test
112  public void testRegionReplicasCreatedAreDistributed() throws Exception {
113    try {
114      checkAndAssertRegionDistribution(false);
115      // now diesbale and enable the table again. It should be truly distributed
116      HTU.getAdmin().disableTable(table.getName());
117      LOG.info("Disabled the table " + table.getName());
118      LOG.info("enabling the table " + table.getName());
119      HTU.getAdmin().enableTable(table.getName());
120      LOG.info("Enabled the table " + table.getName());
121      boolean res = checkAndAssertRegionDistribution(true);
122      assertTrue(res, "Region retainment not done ");
123    } finally {
124      HTU.getAdmin().disableTable(table.getName());
125      HTU.getAdmin().deleteTable(table.getName());
126    }
127  }
128
129  private boolean checkAndAssertRegionDistribution(boolean checkfourth) throws Exception {
130    Collection<RegionInfo> onlineRegions =
131      new ArrayList<RegionInfo>(getRS().getOnlineRegionsLocalContext().size());
132    for (HRegion region : getRS().getOnlineRegionsLocalContext()) {
133      onlineRegions.add(region.getRegionInfo());
134    }
135    if (this.serverVsOnlineRegions == null) {
136      this.serverVsOnlineRegions = new HashMap<ServerName, Collection<RegionInfo>>();
137      this.serverVsOnlineRegions.put(getRS().getServerName(), onlineRegions);
138    } else {
139      Collection<RegionInfo> existingRegions =
140        new ArrayList<RegionInfo>(this.serverVsOnlineRegions.get(getRS().getServerName()));
141      LOG.info("Count is " + existingRegions.size() + " " + onlineRegions.size());
142      for (RegionInfo existingRegion : existingRegions) {
143        if (!onlineRegions.contains(existingRegion)) {
144          return false;
145        }
146      }
147    }
148    Collection<RegionInfo> onlineRegions2 =
149      new ArrayList<RegionInfo>(getSecondaryRS().getOnlineRegionsLocalContext().size());
150    for (HRegion region : getSecondaryRS().getOnlineRegionsLocalContext()) {
151      onlineRegions2.add(region.getRegionInfo());
152    }
153    if (this.serverVsOnlineRegions2 == null) {
154      this.serverVsOnlineRegions2 = new HashMap<ServerName, Collection<RegionInfo>>();
155      this.serverVsOnlineRegions2.put(getSecondaryRS().getServerName(), onlineRegions2);
156    } else {
157      Collection<RegionInfo> existingRegions = new ArrayList<RegionInfo>(
158        this.serverVsOnlineRegions2.get(getSecondaryRS().getServerName()));
159      LOG.info("Count is " + existingRegions.size() + " " + onlineRegions2.size());
160      for (RegionInfo existingRegion : existingRegions) {
161        if (!onlineRegions2.contains(existingRegion)) {
162          return false;
163        }
164      }
165    }
166    Collection<RegionInfo> onlineRegions3 =
167      new ArrayList<RegionInfo>(getTertiaryRS().getOnlineRegionsLocalContext().size());
168    for (HRegion region : getTertiaryRS().getOnlineRegionsLocalContext()) {
169      onlineRegions3.add(region.getRegionInfo());
170    }
171    if (this.serverVsOnlineRegions3 == null) {
172      this.serverVsOnlineRegions3 = new HashMap<ServerName, Collection<RegionInfo>>();
173      this.serverVsOnlineRegions3.put(getTertiaryRS().getServerName(), onlineRegions3);
174    } else {
175      Collection<RegionInfo> existingRegions =
176        new ArrayList<RegionInfo>(this.serverVsOnlineRegions3.get(getTertiaryRS().getServerName()));
177      LOG.info("Count is " + existingRegions.size() + " " + onlineRegions3.size());
178      for (RegionInfo existingRegion : existingRegions) {
179        if (!onlineRegions3.contains(existingRegion)) {
180          return false;
181        }
182      }
183    }
184    // META and namespace to be added
185    return true;
186  }
187}