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.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.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.jupiter.api.AfterEach; 039import org.junit.jupiter.api.BeforeEach; 040import org.junit.jupiter.api.Tag; 041import org.junit.jupiter.api.Test; 042import org.junit.jupiter.api.TestInfo; 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@Tag(MiscTests.TAG) 053@Tag(MediumTests.TAG) 054public class TestGlobalMemStoreSize { 055 056 private static final Logger LOG = LoggerFactory.getLogger(TestGlobalMemStoreSize.class); 057 private static int regionServerNum = 4; 058 private static int regionNum = 16; 059 // total region num = region num + meta regions 060 private static int totalRegionNum = regionNum + 1; 061 062 private HBaseTestingUtil TEST_UTIL; 063 private SingleProcessHBaseCluster cluster; 064 065 @BeforeEach 066 public void setUp() throws Exception { 067 // Start the cluster 068 LOG.info("Starting cluster"); 069 Configuration conf = HBaseConfiguration.create(); 070 TEST_UTIL = new HBaseTestingUtil(conf); 071 TEST_UTIL.startMiniCluster(regionServerNum); 072 cluster = TEST_UTIL.getHBaseCluster(); 073 LOG.info("Waiting for active/ready master"); 074 cluster.waitForActiveAndReadyMaster(); 075 } 076 077 @AfterEach 078 public void tearDown() throws Exception { 079 TEST_UTIL.shutdownMiniCluster(); 080 } 081 082 /** 083 * Test the global mem store size in the region server is equal to sum of each region's mem store 084 * size 085 */ 086 @Test 087 public void testGlobalMemStore(TestInfo testInfo) throws Exception { 088 // Create a table with regions 089 final TableName table = TableName.valueOf(testInfo.getTestMethod().get().getName()); 090 byte[] family = Bytes.toBytes("family"); 091 LOG.info("Creating table with " + regionNum + " regions"); 092 Table ht = TEST_UTIL.createMultiRegionTable(table, family, regionNum); 093 int numRegions = -1; 094 try (RegionLocator r = TEST_UTIL.getConnection().getRegionLocator(table)) { 095 numRegions = r.getStartKeys().length; 096 } 097 assertEquals(regionNum, numRegions); 098 waitForAllRegionsAssigned(); 099 100 for (HRegionServer server : getOnlineRegionServers()) { 101 long globalMemStoreSize = 0; 102 for (RegionInfo regionInfo : ProtobufUtil.getOnlineRegions(null, server.getRSRpcServices())) { 103 globalMemStoreSize += server.getRegion(regionInfo.getEncodedName()).getMemStoreDataSize(); 104 } 105 assertEquals(server.getRegionServerAccounting().getGlobalMemStoreDataSize(), 106 globalMemStoreSize); 107 } 108 109 // check the global memstore size after flush 110 int i = 0; 111 for (HRegionServer server : getOnlineRegionServers()) { 112 LOG.info("Starting flushes on " + server.getServerName() + ", size=" 113 + server.getRegionServerAccounting().getGlobalMemStoreDataSize()); 114 115 for (RegionInfo regionInfo : 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 = EnvironmentEdgeManager.currentTime(); 121 long timeout = now + 1000; 122 while ( 123 server.getRegionServerAccounting().getGlobalMemStoreDataSize() != 0 124 && timeout < EnvironmentEdgeManager.currentTime() 125 ) { 126 Threads.sleep(10); 127 } 128 long size = server.getRegionServerAccounting().getGlobalMemStoreDataSize(); 129 if (size > 0) { 130 // If size > 0, see if its because the meta region got edits while 131 // our test was running.... 132 for (RegionInfo regionInfo : ProtobufUtil.getOnlineRegions(null, 133 server.getRSRpcServices())) { 134 HRegion r = server.getRegion(regionInfo.getEncodedName()); 135 long l = r.getMemStoreDataSize(); 136 if (l > 0) { 137 // Only meta could have edits at this stage. Give it another flush 138 // clear them. 139 assertTrue(regionInfo.isMetaRegion()); 140 LOG.info(r.toString() + " " + l + ", reflushing"); 141 r.flush(true); 142 } 143 } 144 } 145 size = server.getRegionServerAccounting().getGlobalMemStoreDataSize(); 146 assertEquals(0, size, "Server=" + server.getServerName() + ", i=" + i++); 147 } 148 149 ht.close(); 150 } 151 152 /** 153 * Flush and log stats on flush 154 */ 155 private void flush(final HRegion r, final HRegionServer server) throws IOException { 156 LOG.info("Flush " + r.toString() + " on " + server.getServerName() + ", " + r.flush(true) 157 + ", size=" + server.getRegionServerAccounting().getGlobalMemStoreDataSize()); 158 } 159 160 private List<HRegionServer> getOnlineRegionServers() { 161 List<HRegionServer> list = new ArrayList<>(); 162 for (JVMClusterUtil.RegionServerThread rst : cluster.getRegionServerThreads()) { 163 if (rst.getRegionServer().isOnline()) { 164 list.add(rst.getRegionServer()); 165 } 166 } 167 return list; 168 } 169 170 /** 171 * Wait until all the regions are assigned. 172 */ 173 private void waitForAllRegionsAssigned() throws IOException { 174 while (true) { 175 int regionCount = HBaseTestingUtil.getAllOnlineRegions(cluster).size(); 176 if (regionCount >= totalRegionNum) break; 177 LOG.debug("Waiting for there to be " + totalRegionNum + " regions, but there are " 178 + regionCount + " right now."); 179 try { 180 Thread.sleep(100); 181 } catch (InterruptedException e) { 182 } 183 } 184 } 185}