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.assertEquals;
021import static org.mockito.Mockito.mock;
022import static org.mockito.Mockito.when;
023
024import java.util.ArrayList;
025import java.util.List;
026import java.util.OptionalDouble;
027import java.util.OptionalLong;
028import java.util.concurrent.atomic.AtomicInteger;
029import org.apache.hadoop.conf.Configuration;
030import org.apache.hadoop.hbase.HBaseConfiguration;
031import org.apache.hadoop.hbase.HConstants;
032import org.apache.hadoop.hbase.HDFSBlocksDistribution;
033import org.apache.hadoop.hbase.ServerName;
034import org.apache.hadoop.hbase.client.RegionInfo;
035import org.apache.hadoop.hbase.ipc.RpcServerInterface;
036import org.apache.hadoop.hbase.testclassification.RegionServerTests;
037import org.apache.hadoop.hbase.testclassification.SmallTests;
038import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
039import org.apache.hadoop.hbase.util.ManualEnvironmentEdge;
040import org.apache.hadoop.hbase.wal.WALFactory;
041import org.apache.hadoop.hbase.wal.WALProvider;
042import org.junit.jupiter.api.Tag;
043import org.junit.jupiter.api.Test;
044import org.mockito.Mockito;
045import org.mockito.stubbing.Answer;
046
047import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
048
049@Tag(SmallTests.TAG)
050@Tag(RegionServerTests.TAG)
051public class TestMetricsRegionServerAggregate {
052
053  @Test
054  public void test() {
055    AtomicInteger retVal = new AtomicInteger(0);
056    Answer defaultAnswer = invocation -> {
057      Class<?> returnType = invocation.getMethod().getReturnType();
058
059      if (returnType.equals(Integer.TYPE) || returnType.equals(Integer.class)) {
060        return retVal.get();
061      } else if (returnType.equals(Long.TYPE) || returnType.equals(Long.class)) {
062        return (long) retVal.get();
063      }
064      return Mockito.RETURNS_DEFAULTS.answer(invocation);
065    };
066
067    ServerName serverName = mock(ServerName.class);
068    when(serverName.getHostname()).thenReturn("foo");
069    WALFactory walFactory = mock(WALFactory.class);
070    RpcServerInterface rpcServer = mock(RpcServerInterface.class);
071    AtomicInteger storeFileCount = new AtomicInteger(1);
072    HRegion regionOne = getMockedRegion(defaultAnswer, "a", "foo", true, storeFileCount);
073    HRegion regionTwo = getMockedRegion(defaultAnswer, "b", "bar", true, storeFileCount);
074    HRegion regionThree = getMockedRegion(defaultAnswer, "c", "foo", false, storeFileCount);
075    HRegion regionFour = getMockedRegion(defaultAnswer, "d", "bar", false, storeFileCount);
076    List<HRegion> regions = Lists.newArrayList(regionOne, regionTwo, regionThree, regionFour);
077
078    int numStoresPerRegion = 2;
079    for (HRegion region : regions) {
080      // if adding more stores, update numStoresPerRegion so that tests below continue working
081      assertEquals(numStoresPerRegion, region.getStores().size());
082    }
083
084    HRegionServer regionServer = mock(HRegionServer.class, defaultAnswer);
085    when(regionServer.getWalFactory()).thenReturn(walFactory);
086    when(regionServer.getOnlineRegionsLocalContext()).thenReturn(regions);
087    when(regionServer.getServerName()).thenReturn(serverName);
088    Configuration conf = HBaseConfiguration.create();
089    int metricsPeriodSec = 600;
090    // set a very long period so that it doesn't actually run during our very quick test
091    conf.setLong(HConstants.REGIONSERVER_METRICS_PERIOD, metricsPeriodSec * 1000);
092    when(regionServer.getConfiguration()).thenReturn(conf);
093    when(regionServer.getRpcServer()).thenReturn(rpcServer);
094
095    MetricsRegionServerWrapperImpl wrapper = new MetricsRegionServerWrapperImpl(regionServer);
096
097    // we need to control the edge because rate calculations expect a
098    // stable interval relative to the configured period
099    ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
100    EnvironmentEdgeManager.injectEdge(edge);
101
102    try {
103      for (int i = 1; i <= 10; i++) {
104        edge.incValue(wrapper.getPeriod());
105        retVal.incrementAndGet();
106        wrapper.forceRecompute();
107
108        int numRegions = regions.size();
109        int totalStores = numRegions * numStoresPerRegion;
110
111        // there are N regions, and each has M stores. everything gets aggregated, so
112        // multiply expected values accordingly
113        int expectedForRegions = retVal.get() * numRegions;
114        int expectedForStores = retVal.get() * totalStores;
115
116        assertEquals(totalStores, wrapper.getNumStores());
117        assertEquals(expectedForStores, wrapper.getFlushedCellsCount());
118        assertEquals(expectedForStores, wrapper.getCompactedCellsCount());
119        assertEquals(expectedForStores, wrapper.getMajorCompactedCellsCount());
120        assertEquals(expectedForStores, wrapper.getFlushedCellsSize());
121        assertEquals(expectedForStores, wrapper.getCompactedCellsSize());
122        assertEquals(expectedForStores, wrapper.getMajorCompactedCellsSize());
123        assertEquals(expectedForRegions, wrapper.getCellsCountCompactedFromMob());
124        assertEquals(expectedForRegions, wrapper.getCellsCountCompactedToMob());
125        assertEquals(expectedForRegions, wrapper.getCellsSizeCompactedFromMob());
126        assertEquals(expectedForRegions, wrapper.getCellsSizeCompactedToMob());
127        assertEquals(expectedForRegions, wrapper.getMobFlushCount());
128        assertEquals(expectedForRegions, wrapper.getMobFlushedCellsCount());
129        assertEquals(expectedForRegions, wrapper.getMobFlushedCellsSize());
130        assertEquals(expectedForRegions, wrapper.getMobScanCellsCount());
131        assertEquals(expectedForRegions, wrapper.getMobScanCellsSize());
132        assertEquals(expectedForRegions, wrapper.getCheckAndMutateChecksFailed());
133        assertEquals(expectedForRegions, wrapper.getCheckAndMutateChecksPassed());
134        assertEquals(expectedForStores, wrapper.getStoreFileIndexSize());
135        assertEquals(expectedForStores, wrapper.getTotalStaticIndexSize());
136        assertEquals(expectedForStores, wrapper.getTotalStaticBloomSize());
137        assertEquals(expectedForStores, wrapper.getBloomFilterRequestsCount());
138        assertEquals(expectedForStores, wrapper.getBloomFilterNegativeResultsCount());
139        assertEquals(expectedForStores, wrapper.getBloomFilterEligibleRequestsCount());
140        assertEquals(expectedForRegions, wrapper.getNumMutationsWithoutWAL());
141        assertEquals(expectedForRegions, wrapper.getDataInMemoryWithoutWAL());
142        assertEquals(expectedForRegions, wrapper.getAverageRegionSize());
143        assertEquals(expectedForRegions, wrapper.getBlockedRequestsCount());
144        assertEquals(expectedForStores, wrapper.getNumReferenceFiles());
145        assertEquals(expectedForStores, wrapper.getMemStoreSize());
146        assertEquals(expectedForStores, wrapper.getOnHeapMemStoreSize());
147        assertEquals(expectedForStores, wrapper.getOffHeapMemStoreSize());
148        assertEquals(expectedForStores, wrapper.getStoreFileSize());
149        assertEquals(expectedForRegions, wrapper.getReadRequestsCount());
150        assertEquals(expectedForRegions, wrapper.getCpRequestsCount());
151        assertEquals(expectedForRegions, wrapper.getFilteredReadRequestsCount());
152        assertEquals(expectedForRegions, wrapper.getWriteRequestsCount());
153        assertEquals(expectedForRegions * 2, wrapper.getTotalRowActionRequestCount());
154
155        // If we have N regions, each with M stores. That's N*M stores in total. In creating those
156        // stores, we increment the number and age of storefiles for each one. So the first
157        // store has 1 file of 1 age, then 2 files of 2 age, etc.
158        // formula for 1+2+3..+n
159        assertEquals((totalStores * (totalStores + 1)) / 2, wrapper.getNumStoreFiles());
160        assertEquals(totalStores, wrapper.getMaxStoreFiles());
161        assertEquals(totalStores, wrapper.getMaxStoreFileAge());
162        assertEquals(1, wrapper.getMinStoreFileAge());
163        assertEquals(totalStores / 2, wrapper.getAvgStoreFileAge());
164
165        // there are four regions, two are primary and the other two secondary
166        // for each type, one region has 100% locality, the other has 0%.
167        // this just proves we correctly aggregate for each
168        assertEquals(50.0, wrapper.getPercentFileLocal(), 0.0001);
169        assertEquals(50.0, wrapper.getPercentFileLocalSecondaryRegions(), 0.0001);
170
171        // readRequestCount and writeRequestCount are tracking the value of i, which increases by 1
172        // each interval. There are N regions, so the delta each interval is N*i=N. So the rate is
173        // simply N / period.
174        assertEquals((double) numRegions / metricsPeriodSec, wrapper.getReadRequestsRatePerSecond(),
175          0.0001);
176        assertEquals((double) numRegions / metricsPeriodSec,
177          wrapper.getWriteRequestsRatePerSecond(), 0.0001);
178        // total of above, so multiply by 2
179        assertEquals((double) numRegions / metricsPeriodSec * 2, wrapper.getRequestsPerSecond(),
180          0.0001);
181        // Similar logic to above, except there are M totalStores and each one is of
182        // size tracking i. So the rate is just M / period.
183        assertEquals((double) totalStores / metricsPeriodSec, wrapper.getStoreFileSizeGrowthRate(),
184          0.0001);
185      }
186    } finally {
187      EnvironmentEdgeManager.reset();
188    }
189  }
190
191  @Test
192  public void testWalMetricsForRegionServer() throws InterruptedException {
193    long numLogFiles = 10;
194    long logFileSize = 10240;
195    String hostname = "foo";
196    Configuration conf = HBaseConfiguration.create();
197    conf.setLong(HConstants.REGIONSERVER_METRICS_PERIOD, 1000);
198
199    HRegionServer regionServer = mock(HRegionServer.class);
200    when(regionServer.getConfiguration()).thenReturn(conf);
201    RpcServerInterface rpcServer = mock(RpcServerInterface.class);
202    when(regionServer.getRpcServer()).thenReturn(rpcServer);
203    WALFactory walFactory = mock(WALFactory.class);
204    WALProvider walProvider = mock(WALProvider.class);
205    when(walProvider.getNumLogFiles()).thenReturn(numLogFiles);
206    when(walProvider.getLogFileSize()).thenReturn(logFileSize);
207    List<WALProvider> providers = new ArrayList<>();
208    providers.add(walProvider);
209    when(walFactory.getAllWALProviders()).thenReturn(providers);
210    when(regionServer.getWalFactory()).thenReturn(walFactory);
211    ServerName serverName = mock(ServerName.class);
212    when(serverName.getHostname()).thenReturn(hostname);
213    when(regionServer.getServerName()).thenReturn(serverName);
214
215    MetricsRegionServerWrapperImpl wrapper = new MetricsRegionServerWrapperImpl(regionServer);
216    MetricsRegionServerWrapperImpl.RegionServerMetricsWrapperRunnable runnable =
217      wrapper.new RegionServerMetricsWrapperRunnable();
218    runnable.run();
219    assertEquals(numLogFiles, wrapper.getNumWALFiles());
220    assertEquals(logFileSize, wrapper.getWALFileSize());
221
222    runnable.run();
223    assertEquals(numLogFiles, wrapper.getNumWALFiles());
224    assertEquals(logFileSize, wrapper.getWALFileSize());
225  }
226
227  private HRegion getMockedRegion(Answer defaultAnswer, String name, String localOnHost,
228    boolean isPrimary, AtomicInteger storeFileCount) {
229    RegionInfo regionInfo = mock(RegionInfo.class);
230    when(regionInfo.getEncodedName()).thenReturn(name);
231    if (!isPrimary) {
232      when(regionInfo.getReplicaId()).thenReturn(RegionInfo.DEFAULT_REPLICA_ID + 1);
233    }
234    HDFSBlocksDistribution distribution = new HDFSBlocksDistribution();
235    distribution.addHostsAndBlockWeight(new String[] { localOnHost }, 100);
236
237    HStore store = getMockedStore(HStore.class, defaultAnswer, storeFileCount);
238    HMobStore mobStore = getMockedStore(HMobStore.class, defaultAnswer, storeFileCount);
239
240    HRegion region = mock(HRegion.class, defaultAnswer);
241    when(region.getRegionInfo()).thenReturn(regionInfo);
242    when(region.getHDFSBlocksDistribution()).thenReturn(distribution);
243    when(region.getStores()).thenReturn(Lists.newArrayList(store, mobStore));
244    return region;
245  }
246
247  private <T extends HStore> T getMockedStore(Class<T> clazz, Answer defaultAnswer,
248    AtomicInteger storeFileCount) {
249    T store = mock(clazz, defaultAnswer);
250    int storeFileCountVal = storeFileCount.getAndIncrement();
251    when(store.getStorefilesCount()).thenReturn(storeFileCountVal);
252    when(store.getAvgStoreFileAge()).thenReturn(OptionalDouble.of(storeFileCountVal));
253    when(store.getMaxStoreFileAge()).thenReturn(OptionalLong.of(storeFileCountVal));
254    when(store.getMinStoreFileAge()).thenReturn(OptionalLong.of(storeFileCountVal));
255    MemStoreSize memStore = mock(MemStoreSize.class, defaultAnswer);
256    when(store.getMemStoreSize()).thenReturn(memStore);
257    return store;
258  }
259
260}