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.EnvironmentEdgeManager;
036import org.apache.hadoop.hbase.util.JVMClusterUtil;
037import org.apache.hadoop.hbase.util.Threads;
038import org.junit.ClassRule;
039import org.junit.Rule;
040import org.junit.Test;
041import org.junit.experimental.categories.Category;
042import org.junit.rules.TestName;
043import org.slf4j.Logger;
044import org.slf4j.LoggerFactory;
045
046import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
047
048/**
049 * Test HBASE-3694 whether the GlobalMemStoreSize is the same as the summary of all the online
050 * region's MemStoreSize
051 */
052@Category({ MiscTests.class, MediumTests.class })
053public class TestGlobalMemStoreSize {
054
055  @ClassRule
056  public static final HBaseClassTestRule CLASS_RULE =
057    HBaseClassTestRule.forClass(TestGlobalMemStoreSize.class);
058
059  private static final Logger LOG = LoggerFactory.getLogger(TestGlobalMemStoreSize.class);
060  private static int regionServerNum = 4;
061  private static int regionNum = 16;
062  // total region num = region num + meta regions
063  private static int totalRegionNum = regionNum + 1;
064
065  private HBaseTestingUtil TEST_UTIL;
066  private SingleProcessHBaseCluster cluster;
067
068  @Rule
069  public TestName name = new TestName();
070
071  /**
072   * Test the global mem store size in the region server is equal to sum of each region's mem store
073   * size
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 HBaseTestingUtil(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 : ProtobufUtil.getOnlineRegions(null, server.getRSRpcServices())) {
101        globalMemStoreSize += server.getRegion(regionInfo.getEncodedName()).getMemStoreDataSize();
102      }
103      assertEquals(server.getRegionServerAccounting().getGlobalMemStoreDataSize(),
104        globalMemStoreSize);
105    }
106
107    // check the global memstore size after flush
108    int i = 0;
109    for (HRegionServer server : getOnlineRegionServers()) {
110      LOG.info("Starting flushes on " + server.getServerName() + ", size="
111        + server.getRegionServerAccounting().getGlobalMemStoreDataSize());
112
113      for (RegionInfo regionInfo : ProtobufUtil.getOnlineRegions(null, server.getRSRpcServices())) {
114        HRegion r = server.getRegion(regionInfo.getEncodedName());
115        flush(r, server);
116      }
117      LOG.info("Post flush on " + server.getServerName());
118      long now = EnvironmentEdgeManager.currentTime();
119      long timeout = now + 1000;
120      while (
121        server.getRegionServerAccounting().getGlobalMemStoreDataSize() != 0
122          && timeout < EnvironmentEdgeManager.currentTime()
123      ) {
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 : ProtobufUtil.getOnlineRegions(null,
131          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   */
154  private void flush(final HRegion r, final HRegionServer server) throws IOException {
155    LOG.info("Flush " + r.toString() + " on " + server.getServerName() + ", " + r.flush(true)
156      + ", size=" + server.getRegionServerAccounting().getGlobalMemStoreDataSize());
157  }
158
159  private List<HRegionServer> getOnlineRegionServers() {
160    List<HRegionServer> list = new ArrayList<>();
161    for (JVMClusterUtil.RegionServerThread rst : cluster.getRegionServerThreads()) {
162      if (rst.getRegionServer().isOnline()) {
163        list.add(rst.getRegionServer());
164      }
165    }
166    return list;
167  }
168
169  /**
170   * Wait until all the regions are assigned.
171   */
172  private void waitForAllRegionsAssigned() throws IOException {
173    while (true) {
174      int regionCount = HBaseTestingUtil.getAllOnlineRegions(cluster).size();
175      if (regionCount >= totalRegionNum) break;
176      LOG.debug("Waiting for there to be " + totalRegionNum + " regions, but there are "
177        + regionCount + " right now.");
178      try {
179        Thread.sleep(100);
180      } catch (InterruptedException e) {
181      }
182    }
183  }
184}