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 java.io.Closeable;
021import java.io.IOException;
022import java.util.HashMap;
023import java.util.Map;
024import java.util.OptionalDouble;
025import java.util.OptionalLong;
026import java.util.concurrent.ScheduledExecutorService;
027import java.util.concurrent.ScheduledFuture;
028import java.util.concurrent.TimeUnit;
029import org.apache.commons.lang3.mutable.MutableLong;
030import org.apache.hadoop.hbase.CompatibilitySingletonFactory;
031import org.apache.hadoop.hbase.client.RegionInfo;
032import org.apache.hadoop.hbase.client.TableDescriptor;
033import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
034import org.apache.hadoop.hbase.util.Pair;
035import org.apache.hadoop.metrics2.MetricsExecutor;
036import org.apache.yetus.audience.InterfaceAudience;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040@InterfaceAudience.Private
041public class MetricsRegionWrapperImpl implements MetricsRegionWrapper, Closeable {
042
043  private static final Logger LOG = LoggerFactory.getLogger(MetricsRegionWrapperImpl.class);
044
045  public static final int PERIOD = 45;
046  public static final String UNKNOWN = "unknown";
047
048  private final HRegion region;
049  private ScheduledExecutorService executor;
050  private Runnable runnable;
051  private long numStoreFiles;
052  private long storeRefCount;
053  private long maxCompactedStoreFileRefCount;
054  private long memstoreSize;
055  private long memstoreHeapSize;
056  private long memstoreOffHeapSize;
057  private long storeFileSize;
058  private long maxStoreFileAge;
059  private long minStoreFileAge;
060  private long avgStoreFileAge;
061  private long numReferenceFiles;
062  private long maxFlushQueueSize;
063  private long maxCompactionQueueSize;
064  private Map<String, Long> readsOnlyFromMemstore;
065  private Map<String, Long> mixedReadsOnStore;
066
067  private ScheduledFuture<?> regionMetricsUpdateTask;
068
069  private float currentRegionCacheRatio;
070  private final String tableDescriptorHash;
071
072  private float currentRegionColdDataRatio;
073
074  public MetricsRegionWrapperImpl(HRegion region) {
075    this.region = region;
076    this.tableDescriptorHash = determineTableDescriptorHash();
077    this.executor = CompatibilitySingletonFactory.getInstance(MetricsExecutor.class).getExecutor();
078    this.runnable = new HRegionMetricsWrapperRunnable();
079    this.regionMetricsUpdateTask =
080      this.executor.scheduleWithFixedDelay(this.runnable, PERIOD, PERIOD, TimeUnit.SECONDS);
081  }
082
083  @Override
084  public String getTableName() {
085    TableDescriptor tableDesc = this.region.getTableDescriptor();
086    if (tableDesc == null) {
087      return UNKNOWN;
088    }
089    return tableDesc.getTableName().getQualifierAsString();
090  }
091
092  @Override
093  public String getNamespace() {
094    TableDescriptor tableDesc = this.region.getTableDescriptor();
095    if (tableDesc == null) {
096      return UNKNOWN;
097    }
098    return tableDesc.getTableName().getNamespaceAsString();
099  }
100
101  @Override
102  public String getRegionName() {
103    RegionInfo regionInfo = this.region.getRegionInfo();
104    if (regionInfo == null) {
105      return UNKNOWN;
106    }
107    return regionInfo.getEncodedName();
108  }
109
110  @Override
111  public long getNumStores() {
112    Map<byte[], HStore> stores = this.region.stores;
113    if (stores == null) {
114      return 0;
115    }
116    return stores.size();
117  }
118
119  @Override
120  public long getNumStoreFiles() {
121    return numStoreFiles;
122  }
123
124  @Override
125  public long getMemStoreSize() {
126    return memstoreSize;
127  }
128
129  @Override
130  public long getMemStoreHeapSize() {
131    return memstoreHeapSize;
132  }
133
134  @Override
135  public long getMemStoreOffHeapSize() {
136    return memstoreOffHeapSize;
137  }
138
139  @Override
140  public long getStoreFileSize() {
141    return storeFileSize;
142  }
143
144  public float getCurrentRegionCacheRatio() {
145    return currentRegionCacheRatio;
146  }
147
148  public float getCurrentRegionColdDataRatio() {
149    return currentRegionColdDataRatio;
150  }
151
152  @Override
153  public long getStoreRefCount() {
154    return storeRefCount;
155  }
156
157  @Override
158  public long getMaxCompactedStoreFileRefCount() {
159    return maxCompactedStoreFileRefCount;
160  }
161
162  @Override
163  public long getReadRequestCount() {
164    return this.region.getReadRequestsCount();
165  }
166
167  @Override
168  public long getCpRequestCount() {
169    return this.region.getCpRequestsCount();
170  }
171
172  @Override
173  public long getFilteredReadRequestCount() {
174    return this.region.getFilteredReadRequestsCount();
175  }
176
177  @Override
178  public long getWriteRequestCount() {
179    return this.region.getWriteRequestsCount();
180  }
181
182  @Override
183  public long getNumFilesCompacted() {
184    return this.region.compactionNumFilesCompacted.sum();
185  }
186
187  @Override
188  public long getNumBytesCompacted() {
189    return this.region.compactionNumBytesCompacted.sum();
190  }
191
192  @Override
193  public long getNumCompactionsCompleted() {
194    return this.region.compactionsFinished.sum();
195  }
196
197  @Override
198  public long getLastMajorCompactionAge() {
199    long lastMajorCompactionTs = 0L;
200    try {
201      lastMajorCompactionTs = this.region.getOldestHfileTs(true);
202    } catch (IOException ioe) {
203      LOG.error("Could not load HFile info ", ioe);
204    }
205    long now = EnvironmentEdgeManager.currentTime();
206    return now - lastMajorCompactionTs;
207  }
208
209  @Override
210  public long getTotalRequestCount() {
211    return getReadRequestCount() + getWriteRequestCount();
212  }
213
214  @Override
215  public long getNumCompactionsFailed() {
216    return this.region.compactionsFailed.sum();
217  }
218
219  @Override
220  public long getNumCompactionsQueued() {
221    return this.region.compactionsQueued.sum();
222  }
223
224  @Override
225  public long getNumFlushesQueued() {
226    return this.region.flushesQueued.sum();
227  }
228
229  @Override
230  public long getMaxCompactionQueueSize() {
231    return maxCompactionQueueSize;
232  }
233
234  @Override
235  public long getMaxFlushQueueSize() {
236    return maxFlushQueueSize;
237  }
238
239  @Override
240  public long getMaxStoreFileAge() {
241    return maxStoreFileAge;
242  }
243
244  @Override
245  public long getMinStoreFileAge() {
246    return minStoreFileAge;
247  }
248
249  @Override
250  public long getAvgStoreFileAge() {
251    return avgStoreFileAge;
252  }
253
254  @Override
255  public long getNumReferenceFiles() {
256    return numReferenceFiles;
257  }
258
259  @Override
260  public int getRegionHashCode() {
261    return this.region.hashCode();
262  }
263
264  @Override
265  public Map<String, Long> getMemstoreOnlyRowReadsCount() {
266    return readsOnlyFromMemstore;
267  }
268
269  @Override
270  public Map<String, Long> getMixedRowReadsCount() {
271    return mixedReadsOnStore;
272  }
273
274  public class HRegionMetricsWrapperRunnable implements Runnable {
275
276    @Override
277    public void run() {
278      // The executor only reschedules this task (scheduleWithFixedDelay) if run() returns
279      // normally. Any Throwable that escapes here (e.g. an NPE from a region closing mid-scan)
280      // is swallowed by the underlying ScheduledFutureTask with no log output anywhere, and this
281      // task permanently stops running. Catch everything so a failure is visible AND the next
282      // scheduled run still happens.
283      try {
284        long tempNumStoreFiles = 0;
285        int tempStoreRefCount = 0;
286        int tempMaxCompactedStoreFileRefCount = 0;
287        long tempMemstoreSize = 0;
288        long tempMemstoreHeapSize = 0;
289        long tempMemstoreOffHeapSize = 0;
290        long tempStoreFileSize = 0;
291        long tempMaxStoreFileAge = 0;
292        long tempMinStoreFileAge = Long.MAX_VALUE;
293        long tempNumReferenceFiles = 0;
294        long tempMaxCompactionQueueSize = 0;
295        long tempMaxFlushQueueSize = 0;
296        long avgAgeNumerator = 0;
297        long numHFiles = 0;
298        if (region.stores != null) {
299          for (HStore store : region.stores.values()) {
300            tempNumStoreFiles += store.getStorefilesCount();
301            int currentStoreRefCount = store.getStoreRefCount();
302            tempStoreRefCount += currentStoreRefCount;
303            int currentMaxCompactedStoreFileRefCount = store.getMaxCompactedStoreFileRefCount();
304            tempMaxCompactedStoreFileRefCount =
305              Math.max(tempMaxCompactedStoreFileRefCount, currentMaxCompactedStoreFileRefCount);
306            final MemStoreSize memStore = store.getMemStoreSize();
307            tempMemstoreSize += memStore.getDataSize();
308            tempMemstoreHeapSize += memStore.getHeapSize();
309            tempMemstoreOffHeapSize += memStore.getOffHeapSize();
310            tempStoreFileSize += store.getStorefilesSize();
311            OptionalLong storeMaxStoreFileAge = store.getMaxStoreFileAge();
312            if (
313              storeMaxStoreFileAge.isPresent()
314                && storeMaxStoreFileAge.getAsLong() > tempMaxStoreFileAge
315            ) {
316              tempMaxStoreFileAge = storeMaxStoreFileAge.getAsLong();
317            }
318
319            OptionalLong storeMinStoreFileAge = store.getMinStoreFileAge();
320            if (
321              storeMinStoreFileAge.isPresent()
322                && storeMinStoreFileAge.getAsLong() < tempMinStoreFileAge
323            ) {
324              tempMinStoreFileAge = storeMinStoreFileAge.getAsLong();
325            }
326
327            long storeHFiles = store.getNumHFiles();
328            numHFiles += storeHFiles;
329            tempNumReferenceFiles += store.getNumReferenceFiles();
330
331            OptionalDouble storeAvgStoreFileAge = store.getAvgStoreFileAge();
332            if (storeAvgStoreFileAge.isPresent()) {
333              avgAgeNumerator += (long) storeAvgStoreFileAge.getAsDouble() * storeHFiles;
334            }
335            if (mixedReadsOnStore == null) {
336              mixedReadsOnStore = new HashMap<String, Long>();
337            }
338            Long tempVal = mixedReadsOnStore.get(store.getColumnFamilyName());
339            if (tempVal == null) {
340              tempVal = 0L;
341            } else {
342              tempVal += store.getMixedRowReadsCount();
343            }
344            mixedReadsOnStore.put(store.getColumnFamilyName(), tempVal);
345            if (readsOnlyFromMemstore == null) {
346              readsOnlyFromMemstore = new HashMap<String, Long>();
347            }
348            tempVal = readsOnlyFromMemstore.get(store.getColumnFamilyName());
349            if (tempVal == null) {
350              tempVal = 0L;
351            } else {
352              tempVal += store.getMemstoreOnlyRowReadsCount();
353            }
354            readsOnlyFromMemstore.put(store.getColumnFamilyName(), tempVal);
355          }
356        }
357        MutableLong regionCachedAmount = new MutableLong(0);
358        region.getBlockCache().getRegionCachedInfo()
359          .ifPresent(regionCacheRatio -> regionCachedAmount
360            .addAndGet(regionCacheRatio.getOrDefault(region.getRegionInfo().getEncodedName(), 0L)));
361        if (tempStoreFileSize > 0) {
362          LOG.debug("Region {}, had cached {} bytes from a total of {}",
363            region.getRegionInfo().getEncodedName(), regionCachedAmount.getValue(),
364            tempStoreFileSize);
365          currentRegionCacheRatio = regionCachedAmount.floatValue() / tempStoreFileSize;
366          if (DataTieringManager.getInstance() != null) {
367            currentRegionColdDataRatio = DataTieringManager.getInstance().getRegionColdDataSize()
368              .getOrDefault(region.getRegionInfo().getEncodedName(), new Pair<>(null, 0L))
369              .getSecond() / (float) tempStoreFileSize;
370          }
371        }
372        numStoreFiles = tempNumStoreFiles;
373        storeRefCount = tempStoreRefCount;
374        maxCompactedStoreFileRefCount = tempMaxCompactedStoreFileRefCount;
375        memstoreSize = tempMemstoreSize;
376        memstoreHeapSize = tempMemstoreHeapSize;
377        memstoreOffHeapSize = tempMemstoreOffHeapSize;
378        storeFileSize = tempStoreFileSize;
379        maxStoreFileAge = tempMaxStoreFileAge;
380        if (tempMinStoreFileAge != Long.MAX_VALUE) {
381          minStoreFileAge = tempMinStoreFileAge;
382        }
383
384        if (numHFiles != 0) {
385          avgStoreFileAge = avgAgeNumerator / numHFiles;
386        }
387
388        numReferenceFiles = tempNumReferenceFiles;
389        tempMaxCompactionQueueSize = getNumCompactionsQueued();
390        tempMaxFlushQueueSize = getNumFlushesQueued();
391        if (tempMaxCompactionQueueSize > maxCompactionQueueSize) {
392          maxCompactionQueueSize = tempMaxCompactionQueueSize;
393        }
394        if (tempMaxFlushQueueSize > maxFlushQueueSize) {
395          maxFlushQueueSize = tempMaxFlushQueueSize;
396        }
397      } catch (Throwable t) {
398        LOG.error("Failed to compute per-region metrics; will retry on next scheduled run", t);
399      }
400    }
401  }
402
403  @Override
404  public String getTableDescriptorHash() {
405    return tableDescriptorHash;
406  }
407
408  private String determineTableDescriptorHash() {
409    TableDescriptor tableDesc = this.region.getTableDescriptor();
410    if (tableDesc == null) {
411      return UNKNOWN;
412    }
413    return tableDesc.getDescriptorHash();
414  }
415
416  @Override
417  public void close() throws IOException {
418    regionMetricsUpdateTask.cancel(true);
419  }
420
421  /**
422   * Get the replica id of this region.
423   */
424  @Override
425  public int getReplicaId() {
426    return region.getRegionInfo().getReplicaId();
427  }
428
429}