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.Set;
025import java.util.concurrent.ConcurrentHashMap;
026import java.util.concurrent.ScheduledExecutorService;
027import java.util.concurrent.ScheduledFuture;
028import java.util.concurrent.TimeUnit;
029import org.apache.hadoop.hbase.CompatibilitySingletonFactory;
030import org.apache.hadoop.hbase.HConstants;
031import org.apache.hadoop.hbase.TableName;
032import org.apache.hadoop.metrics2.MetricsExecutor;
033import org.apache.yetus.audience.InterfaceAudience;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
038
039@InterfaceAudience.Private
040public class MetricsTableWrapperAggregateImpl implements MetricsTableWrapperAggregate, Closeable {
041  private static final Logger LOG = LoggerFactory.getLogger(MetricsTableWrapperAggregateImpl.class);
042  private final HRegionServer regionServer;
043  private ScheduledExecutorService executor;
044  private Runnable runnable;
045  private long period;
046  private ScheduledFuture<?> tableMetricsUpdateTask;
047  private ConcurrentHashMap<TableName, MetricsTableValues> metricsTableMap =
048    new ConcurrentHashMap<>();
049
050  public MetricsTableWrapperAggregateImpl(final HRegionServer regionServer) {
051    this.regionServer = regionServer;
052    this.period = regionServer.getConfiguration().getLong(HConstants.REGIONSERVER_METRICS_PERIOD,
053      HConstants.DEFAULT_REGIONSERVER_METRICS_PERIOD) + 1000;
054    this.executor = CompatibilitySingletonFactory.getInstance(MetricsExecutor.class).getExecutor();
055    this.runnable = new TableMetricsWrapperRunnable();
056    this.tableMetricsUpdateTask =
057      this.executor.scheduleWithFixedDelay(this.runnable, period, period, TimeUnit.MILLISECONDS);
058  }
059
060  public class TableMetricsWrapperRunnable implements Runnable {
061
062    @Override
063    public void run() {
064      // The executor only reschedules this task (scheduleWithFixedDelay) if run() returns
065      // normally. Any Throwable that escapes here (e.g. an NPE from a region closing mid-scan)
066      // is swallowed by the underlying ScheduledFutureTask with no log output anywhere, and this
067      // task permanently stops running. Catch everything so a failure is visible AND the next
068      // scheduled run still happens.
069      try {
070        Map<TableName, MetricsTableValues> localMetricsTableMap = new HashMap<>();
071        for (Region r : regionServer.getOnlineRegionsLocalContext()) {
072          TableName tbl = r.getTableDescriptor().getTableName();
073          MetricsTableValues mt = localMetricsTableMap.get(tbl);
074          if (mt == null) {
075            mt = new MetricsTableValues();
076            localMetricsTableMap.put(tbl, mt);
077          }
078          long memstoreReadCount = 0L;
079          long mixedReadCount = 0L;
080          String tempKey = null;
081          if (r.getStores() != null) {
082            String familyName = null;
083            for (Store store : r.getStores()) {
084              familyName = store.getColumnFamilyName();
085
086              mt.storeFileCount += store.getStorefilesCount();
087              mt.maxStoreFileCount = Math.max(mt.maxStoreFileCount, store.getStorefilesCount());
088              final MemStoreSize memstoreSize = store.getMemStoreSize();
089              mt.memstoreSize += memstoreSize.getDataSize();
090              mt.memstoreHeapSize += memstoreSize.getHeapSize();
091              mt.memstoreOffHeapSize += memstoreSize.getOffHeapSize();
092              mt.storeFileSize += store.getStorefilesSize();
093              mt.referenceFileCount += store.getNumReferenceFiles();
094              if (store.getMaxStoreFileAge().isPresent()) {
095                mt.maxStoreFileAge =
096                  Math.max(mt.maxStoreFileAge, store.getMaxStoreFileAge().getAsLong());
097              }
098              if (store.getMinStoreFileAge().isPresent()) {
099                mt.minStoreFileAge =
100                  Math.min(mt.minStoreFileAge, store.getMinStoreFileAge().getAsLong());
101              }
102              if (store.getAvgStoreFileAge().isPresent()) {
103                mt.totalStoreFileAge +=
104                  (long) (store.getAvgStoreFileAge().getAsDouble() * store.getStorefilesCount());
105              }
106              mt.storeCount += 1;
107
108              mt.staticIndexSize += store.getTotalStaticIndexSize();
109              mt.staticBloomSize += store.getTotalStaticBloomSize();
110
111              mt.bloomRequestsCount += store.getBloomFilterRequestsCount();
112              mt.bloomNegativeResultsCount += store.getBloomFilterNegativeResultsCount();
113              mt.bloomEligibleRequestsCount += store.getBloomFilterEligibleRequestsCount();
114
115              tempKey = tbl.getNameAsString() + HASH + familyName;
116              Long tempVal = mt.perStoreMemstoreOnlyReadCount.get(tempKey);
117              if (tempVal == null) {
118                tempVal = 0L;
119              }
120              memstoreReadCount = store.getMemstoreOnlyRowReadsCount() + tempVal;
121              tempVal = mt.perStoreMixedReadCount.get(tempKey);
122              if (tempVal == null) {
123                tempVal = 0L;
124              }
125              mixedReadCount = store.getMixedRowReadsCount() + tempVal;
126              // accumulate the count
127              mt.perStoreMemstoreOnlyReadCount.put(tempKey, memstoreReadCount);
128              mt.perStoreMixedReadCount.put(tempKey, mixedReadCount);
129              mt.perStoreFileSize.merge(tempKey, store.getStorefilesSize(), Long::sum);
130            }
131
132            mt.regionCount += 1;
133
134            mt.readRequestCount += r.getReadRequestsCount();
135            mt.filteredReadRequestCount += r.getFilteredReadRequestsCount();
136            mt.writeRequestCount += r.getWriteRequestsCount();
137          }
138        }
139
140        for (Map.Entry<TableName, MetricsTableValues> entry : localMetricsTableMap.entrySet()) {
141          TableName tbl = entry.getKey();
142          if (metricsTableMap.get(tbl) == null) {
143            // this will add the Wrapper to the list of TableMetrics
144            CompatibilitySingletonFactory.getInstance(MetricsRegionServerSourceFactory.class)
145              .getTableAggregate()
146              .getOrCreateTableSource(tbl.getNameAsString(), MetricsTableWrapperAggregateImpl.this);
147          }
148          metricsTableMap.put(entry.getKey(), entry.getValue());
149        }
150        Set<TableName> existingTableNames = Sets.newHashSet(metricsTableMap.keySet());
151        existingTableNames.removeAll(localMetricsTableMap.keySet());
152        MetricsTableAggregateSource agg = CompatibilitySingletonFactory
153          .getInstance(MetricsRegionServerSourceFactory.class).getTableAggregate();
154        for (TableName table : existingTableNames) {
155          agg.deleteTableSource(table.getNameAsString());
156          if (metricsTableMap.get(table) != null) {
157            metricsTableMap.remove(table);
158          }
159        }
160      } catch (Throwable t) {
161        LOG.error("Failed to compute per-table metrics; will retry on next scheduled run", t);
162      }
163    }
164  }
165
166  @Override
167  public long getReadRequestCount(String table) {
168    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
169    if (metricsTable == null) {
170      return 0;
171    } else {
172      return metricsTable.readRequestCount;
173    }
174  }
175
176  @Override
177  public Map<String, Long> getMemstoreOnlyRowReadsCount(String table) {
178    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
179    if (metricsTable == null) {
180      return null;
181    } else {
182      return metricsTable.perStoreMemstoreOnlyReadCount;
183    }
184  }
185
186  @Override
187  public Map<String, Long> getMixedRowReadsCount(String table) {
188    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
189    if (metricsTable == null) {
190      return null;
191    } else {
192      return metricsTable.perStoreMixedReadCount;
193    }
194  }
195
196  @Override
197  public Map<String, Long> getStoreFileSizePerStore(String table) {
198    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
199    if (metricsTable == null) {
200      return null;
201    } else {
202      return metricsTable.perStoreFileSize;
203    }
204  }
205
206  @Override
207  public long getCpRequestsCount(String table) {
208    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
209    if (metricsTable == null) {
210      return 0;
211    } else {
212      return metricsTable.cpRequestCount;
213    }
214  }
215
216  public long getFilteredReadRequestCount(String table) {
217    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
218    if (metricsTable == null) {
219      return 0;
220    }
221    return metricsTable.filteredReadRequestCount;
222  }
223
224  @Override
225  public long getWriteRequestCount(String table) {
226    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
227    if (metricsTable == null) {
228      return 0;
229    } else {
230      return metricsTable.writeRequestCount;
231    }
232  }
233
234  @Override
235  public long getTotalRequestsCount(String table) {
236    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
237    if (metricsTable == null) {
238      return 0;
239    } else {
240      return metricsTable.readRequestCount + metricsTable.writeRequestCount;
241    }
242  }
243
244  @Override
245  public long getMemStoreSize(String table) {
246    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
247    if (metricsTable == null) {
248      return 0;
249    } else {
250      return metricsTable.memstoreSize;
251    }
252  }
253
254  @Override
255  public long getMemStoreHeapSize(String table) {
256    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
257    if (metricsTable == null) {
258      return 0;
259    } else {
260      return metricsTable.memstoreHeapSize;
261    }
262  }
263
264  @Override
265  public long getMemStoreOffHeapSize(String table) {
266    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
267    if (metricsTable == null) {
268      return 0;
269    } else {
270      return metricsTable.memstoreOffHeapSize;
271    }
272  }
273
274  @Override
275  public long getStoreFileSize(String table) {
276    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
277    if (metricsTable == null) {
278      return 0;
279    } else {
280      return metricsTable.storeFileSize;
281    }
282  }
283
284  @Override
285  public long getTableSize(String table) {
286    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
287    if (metricsTable == null) {
288      return 0;
289    } else {
290      return metricsTable.memstoreSize + metricsTable.storeFileSize;
291    }
292  }
293
294  public long getNumRegions(String table) {
295    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
296    if (metricsTable == null) {
297      return 0;
298    }
299    return metricsTable.regionCount;
300  }
301
302  @Override
303  public long getNumStores(String table) {
304    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
305    if (metricsTable == null) {
306      return 0;
307    }
308    return metricsTable.storeCount;
309  }
310
311  @Override
312  public long getNumStoreFiles(String table) {
313    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
314    if (metricsTable == null) {
315      return 0;
316    }
317    return metricsTable.storeFileCount;
318  }
319
320  @Override
321  public long getMaxStoreFiles(String table) {
322    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
323    if (metricsTable == null) {
324      return 0;
325    }
326    return metricsTable.maxStoreFileCount;
327  }
328
329  @Override
330  public long getMaxStoreFileAge(String table) {
331    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
332    if (metricsTable == null) {
333      return 0;
334    }
335    return metricsTable.maxStoreFileAge;
336  }
337
338  @Override
339  public long getMinStoreFileAge(String table) {
340    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
341    if (metricsTable == null) {
342      return 0;
343    }
344    return metricsTable.minStoreFileAge == Long.MAX_VALUE ? 0 : metricsTable.minStoreFileAge;
345  }
346
347  @Override
348  public long getAvgStoreFileAge(String table) {
349    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
350    if (metricsTable == null) {
351      return 0;
352    }
353
354    return metricsTable.storeFileCount == 0
355      ? 0
356      : (metricsTable.totalStoreFileAge / metricsTable.storeFileCount);
357  }
358
359  @Override
360  public long getStaticIndexSize(String table) {
361    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
362    if (metricsTable == null) {
363      return 0;
364    }
365
366    return metricsTable.staticIndexSize;
367  }
368
369  @Override
370  public long getStaticBloomSize(String table) {
371    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
372    if (metricsTable == null) {
373      return 0;
374    }
375
376    return metricsTable.staticBloomSize;
377  }
378
379  @Override
380  public long getBloomFilterRequestsCount(String table) {
381    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
382    if (metricsTable == null) {
383      return 0;
384    }
385
386    return metricsTable.bloomRequestsCount;
387  }
388
389  @Override
390  public long getBloomFilterNegativeResultsCount(String table) {
391    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
392    if (metricsTable == null) {
393      return 0;
394    }
395
396    return metricsTable.bloomNegativeResultsCount;
397  }
398
399  @Override
400  public long getBloomFilterEligibleRequestsCount(String table) {
401    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
402    if (metricsTable == null) {
403      return 0;
404    }
405
406    return metricsTable.bloomEligibleRequestsCount;
407  }
408
409  @Override
410  public long getNumReferenceFiles(String table) {
411    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
412    if (metricsTable == null) {
413      return 0;
414    }
415    return metricsTable.referenceFileCount;
416  }
417
418  @Override
419  public long getAvgRegionSize(String table) {
420    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
421    if (metricsTable == null) {
422      return 0;
423    }
424    return metricsTable.regionCount == 0
425      ? 0
426      : (metricsTable.memstoreSize + metricsTable.storeFileSize) / metricsTable.regionCount;
427  }
428
429  public long getCpRequestCount(String table) {
430    MetricsTableValues metricsTable = metricsTableMap.get(TableName.valueOf(table));
431    if (metricsTable == null) {
432      return 0;
433    }
434    return metricsTable.cpRequestCount;
435  }
436
437  @Override
438  public void close() throws IOException {
439    tableMetricsUpdateTask.cancel(true);
440  }
441
442  private static class MetricsTableValues {
443    long readRequestCount;
444    long filteredReadRequestCount;
445    long writeRequestCount;
446    long memstoreSize;
447    long memstoreHeapSize;
448    long memstoreOffHeapSize;
449    long regionCount;
450    long storeCount;
451    long storeFileCount;
452    long maxStoreFileCount;
453    long storeFileSize;
454    long maxStoreFileAge;
455    long minStoreFileAge = Long.MAX_VALUE;
456    long totalStoreFileAge;
457
458    long staticIndexSize;
459
460    long staticBloomSize;
461    long referenceFileCount;
462
463    long bloomRequestsCount;
464    long bloomNegativeResultsCount;
465    long bloomEligibleRequestsCount;
466    long cpRequestCount;
467    Map<String, Long> perStoreMemstoreOnlyReadCount = new HashMap<>();
468    Map<String, Long> perStoreMixedReadCount = new HashMap<>();
469    Map<String, Long> perStoreFileSize = new HashMap<>();
470  }
471
472}