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;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertTrue;
022
023import java.io.IOException;
024import java.util.ArrayList;
025import java.util.List;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.hbase.client.RegionInfo;
028import org.apache.hadoop.hbase.client.RegionLocator;
029import org.apache.hadoop.hbase.client.Table;
030import org.apache.hadoop.hbase.regionserver.HRegion;
031import org.apache.hadoop.hbase.regionserver.HRegionServer;
032import org.apache.hadoop.hbase.testclassification.MediumTests;
033import org.apache.hadoop.hbase.testclassification.MiscTests;
034import org.apache.hadoop.hbase.util.Bytes;
035import org.apache.hadoop.hbase.util.JVMClusterUtil;
036import org.apache.hadoop.hbase.util.Threads;
037import org.junit.ClassRule;
038import org.junit.Rule;
039import org.junit.Test;
040import org.junit.experimental.categories.Category;
041import org.junit.rules.TestName;
042import org.slf4j.Logger;
043import org.slf4j.LoggerFactory;
044
045import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
046
047/**
048 * Test HBASE-3694 whether the GlobalMemStoreSize is the same as the summary
049 * of all the online region's MemStoreSize
050 */
051@Category({MiscTests.class, MediumTests.class})
052public class TestGlobalMemStoreSize {
053
054  @ClassRule
055  public static final HBaseClassTestRule CLASS_RULE =
056      HBaseClassTestRule.forClass(TestGlobalMemStoreSize.class);
057
058  private static final Logger LOG = LoggerFactory.getLogger(TestGlobalMemStoreSize.class);
059  private static int regionServerNum = 4;
060  private static int regionNum = 16;
061  // total region num = region num + root and meta regions
062  private static int totalRegionNum = regionNum+2;
063
064  private HBaseTestingUtility TEST_UTIL;
065  private MiniHBaseCluster cluster;
066
067  @Rule
068  public TestName name = new TestName();
069
070  /**
071   * Test the global mem store size in the region server is equal to sum of each
072   * region's mem store size
073   * @throws Exception
074   */
075  @Test
076  public void testGlobalMemStore() throws Exception {
077    // Start the cluster
078    LOG.info("Starting cluster");
079    Configuration conf = HBaseConfiguration.create();
080    TEST_UTIL = new HBaseTestingUtility(conf);
081    TEST_UTIL.startMiniCluster(regionServerNum);
082    cluster = TEST_UTIL.getHBaseCluster();
083    LOG.info("Waiting for active/ready master");
084    cluster.waitForActiveAndReadyMaster();
085
086    // Create a table with regions
087    final TableName table = TableName.valueOf(name.getMethodName());
088    byte [] family = Bytes.toBytes("family");
089    LOG.info("Creating table with " + regionNum + " regions");
090    Table ht = TEST_UTIL.createMultiRegionTable(table, family, regionNum);
091    int numRegions = -1;
092    try (RegionLocator r = TEST_UTIL.getConnection().getRegionLocator(table)) {
093      numRegions = r.getStartKeys().length;
094    }
095    assertEquals(regionNum,numRegions);
096    waitForAllRegionsAssigned();
097
098    for (HRegionServer server : getOnlineRegionServers()) {
099      long globalMemStoreSize = 0;
100      for (RegionInfo regionInfo :
101          ProtobufUtil.getOnlineRegions(null, server.getRSRpcServices())) {
102        globalMemStoreSize += server.getRegion(regionInfo.getEncodedName()).getMemStoreDataSize();
103      }
104      assertEquals(server.getRegionServerAccounting().getGlobalMemStoreDataSize(),
105        globalMemStoreSize);
106    }
107
108    // check the global memstore size after flush
109    int i = 0;
110    for (HRegionServer server : getOnlineRegionServers()) {
111      LOG.info("Starting flushes on " + server.getServerName() +
112        ", size=" + server.getRegionServerAccounting().getGlobalMemStoreDataSize());
113
114      for (RegionInfo regionInfo :
115          ProtobufUtil.getOnlineRegions(null, server.getRSRpcServices())) {
116        HRegion r = server.getRegion(regionInfo.getEncodedName());
117        flush(r, server);
118      }
119      LOG.info("Post flush on " + server.getServerName());
120      long now = System.currentTimeMillis();
121      long timeout = now + 1000;
122      while(server.getRegionServerAccounting().getGlobalMemStoreDataSize() != 0 &&
123          timeout < System.currentTimeMillis()) {
124        Threads.sleep(10);
125      }
126      long size = server.getRegionServerAccounting().getGlobalMemStoreDataSize();
127      if (size > 0) {
128        // If size > 0, see if its because the meta region got edits while
129        // our test was running....
130        for (RegionInfo regionInfo :
131            ProtobufUtil.getOnlineRegions(null, server.getRSRpcServices())) {
132          HRegion r = server.getRegion(regionInfo.getEncodedName());
133          long l = r.getMemStoreDataSize();
134          if (l > 0) {
135            // Only meta could have edits at this stage.  Give it another flush
136            // clear them.
137            assertTrue(regionInfo.isMetaRegion());
138            LOG.info(r.toString() + " " + l + ", reflushing");
139            r.flush(true);
140          }
141        }
142      }
143      size = server.getRegionServerAccounting().getGlobalMemStoreDataSize();
144      assertEquals("Server=" + server.getServerName() + ", i=" + i++, 0, size);
145    }
146
147    ht.close();
148    TEST_UTIL.shutdownMiniCluster();
149  }
150
151  /**
152   * Flush and log stats on flush
153   * @param r
154   * @param server
155   * @throws IOException
156   */
157  private void flush(final HRegion r, final HRegionServer server)
158  throws IOException {
159    LOG.info("Flush " + r.toString() + " on " + server.getServerName() +
160      ", " +  r.flush(true) + ", size=" +
161      server.getRegionServerAccounting().getGlobalMemStoreDataSize());
162  }
163
164  private List<HRegionServer> getOnlineRegionServers() {
165    List<HRegionServer> list = new ArrayList<>();
166    for (JVMClusterUtil.RegionServerThread rst :
167          cluster.getRegionServerThreads()) {
168      if (rst.getRegionServer().isOnline()) {
169        list.add(rst.getRegionServer());
170      }
171    }
172    return list;
173  }
174
175  /**
176   * Wait until all the regions are assigned.
177   */
178  private void waitForAllRegionsAssigned() throws IOException {
179    while (true) {
180      int regionCount = HBaseTestingUtility.getAllOnlineRegions(cluster).size();
181      if (regionCount >= totalRegionNum) break;
182      LOG.debug("Waiting for there to be "+ totalRegionNum
183        +" regions, but there are " + regionCount + " right now.");
184      try {
185        Thread.sleep(100);
186      } catch (InterruptedException e) {}
187    }
188  }
189}