1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.regionserver.metrics;
21
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.TreeMap;
30 import java.util.TreeSet;
31 import java.util.concurrent.ConcurrentHashMap;
32 import java.util.concurrent.atomic.AtomicLongArray;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35
36 import org.apache.commons.lang.mutable.MutableDouble;
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 import org.apache.hadoop.conf.Configuration;
40 import org.apache.hadoop.hbase.HConstants;
41 import org.apache.hadoop.hbase.io.hfile.BlockType.BlockCategory;
42 import org.apache.hadoop.hbase.util.Bytes;
43 import org.apache.hadoop.hbase.util.Pair;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 public class SchemaMetrics {
90
91 public interface SchemaAware {
92 public String getTableName();
93 public String getColumnFamilyName();
94 public SchemaMetrics getSchemaMetrics();
95 }
96
97 private static final Log LOG = LogFactory.getLog(SchemaMetrics.class);
98
99 public static enum BlockMetricType {
100
101 READ_TIME("Read", true, true),
102 READ_COUNT("BlockReadCnt", true, false),
103 CACHE_HIT("BlockReadCacheHitCnt", true, false),
104 CACHE_MISS("BlockReadCacheMissCnt", true, false),
105
106 CACHE_SIZE("blockCacheSize", false, false),
107 CACHED("blockCacheNumCached", false, false),
108 EVICTED("blockCacheNumEvicted", false, false);
109
110 private final String metricStr;
111 private final boolean compactionAware;
112 private final boolean timeVarying;
113
114 BlockMetricType(String metricStr, boolean compactionAware,
115 boolean timeVarying) {
116 this.metricStr = metricStr;
117 this.compactionAware = compactionAware;
118 this.timeVarying = timeVarying;
119 }
120
121 @Override
122 public String toString() {
123 return metricStr;
124 }
125
126 private static final String BLOCK_METRIC_TYPE_RE;
127 static {
128 StringBuilder sb = new StringBuilder();
129 for (BlockMetricType bmt : values()) {
130 if (sb.length() > 0)
131 sb.append("|");
132 sb.append(bmt);
133 }
134 BLOCK_METRIC_TYPE_RE = sb.toString();
135 }
136 };
137
138 public static enum StoreMetricType {
139 STORE_FILE_COUNT("storeFileCount"),
140 STORE_FILE_INDEX_SIZE("storeFileIndexSizeMB"),
141 STORE_FILE_SIZE_MB("storeFileSizeMB"),
142 STATIC_BLOOM_SIZE_KB("staticBloomSizeKB"),
143 MEMSTORE_SIZE_MB("memstoreSizeMB"),
144 STATIC_INDEX_SIZE_KB("staticIndexSizeKB"),
145 FLUSH_SIZE("flushSize");
146
147 private final String metricStr;
148
149 StoreMetricType(String metricStr) {
150 this.metricStr = metricStr;
151 }
152
153 @Override
154 public String toString() {
155 return metricStr;
156 }
157 };
158
159
160
161
162
163
164
165
166 public static final String UNKNOWN = "__unknown";
167
168 public static final String TABLE_PREFIX = "tbl.";
169 public static final String CF_PREFIX = "cf.";
170 public static final String BLOCK_TYPE_PREFIX = "bt.";
171 public static final String REGION_PREFIX = "region.";
172
173 public static final String CF_UNKNOWN_PREFIX = CF_PREFIX + UNKNOWN + ".";
174 public static final String CF_BAD_FAMILY_PREFIX = CF_PREFIX + "__badfamily.";
175
176
177 public static final boolean NO_COMPACTION = false;
178
179 public static final String METRIC_GETSIZE = "getsize";
180 public static final String METRIC_NEXTSIZE = "nextsize";
181
182
183
184
185
186
187 public static final String TOTAL_KEY = "";
188
189
190
191
192
193 private static final String META_BLOCK_CATEGORY_STR = "Meta";
194
195 private static final int NUM_BLOCK_CATEGORIES =
196 BlockCategory.values().length;
197
198 private static final int NUM_METRIC_TYPES =
199 BlockMetricType.values().length;
200
201 static final boolean[] BOOL_VALUES = new boolean[] { false, true };
202
203 private static final int NUM_BLOCK_METRICS =
204 NUM_BLOCK_CATEGORIES *
205 BOOL_VALUES.length *
206 NUM_METRIC_TYPES;
207
208 private static final int NUM_STORE_METRIC_TYPES =
209 StoreMetricType.values().length;
210
211
212 private static final String SHOW_TABLE_NAME_CONF_KEY =
213 "hbase.metrics.showTableName";
214
215
216 private static final String MORE_CFS_OMITTED_STR = "__more";
217
218
219
220
221
222 private static final int MAX_METRIC_PREFIX_LENGTH =
223 256 - MORE_CFS_OMITTED_STR.length();
224
225
226
227
228
229
230
231 private static final ConcurrentHashMap<String, SchemaMetrics>
232 tableAndFamilyToMetrics = new ConcurrentHashMap<String, SchemaMetrics>();
233
234
235
236 public static final SchemaMetrics ALL_SCHEMA_METRICS =
237 getInstance(TOTAL_KEY, TOTAL_KEY);
238
239
240 private static final long THRESHOLD_METRICS_FLUSH = 100l;
241
242
243
244
245
246
247 private static volatile Boolean useTableNameGlobally;
248
249
250 private static volatile boolean loggedConfInconsistency;
251
252
253 private final String[] blockMetricNames = new String[NUM_BLOCK_METRICS];
254 private final boolean[] blockMetricTimeVarying =
255 new boolean[NUM_BLOCK_METRICS];
256
257 private final String[] bloomMetricNames = new String[2];
258 private final String[] storeMetricNames = new String[NUM_STORE_METRIC_TYPES];
259 private final String[] storeMetricNamesMax = new String[NUM_STORE_METRIC_TYPES];
260 private final AtomicLongArray onHitCacheMetrics=
261 new AtomicLongArray(NUM_BLOCK_CATEGORIES * BOOL_VALUES.length);
262
263 private SchemaMetrics(final String tableName, final String cfName) {
264 String metricPrefix = SchemaMetrics.generateSchemaMetricsPrefix(
265 tableName, cfName);
266
267 for (BlockCategory blockCategory : BlockCategory.values()) {
268 for (boolean isCompaction : BOOL_VALUES) {
269
270 onHitCacheMetrics.set(getCacheHitMetricIndex(blockCategory, isCompaction), 0);
271
272 for (BlockMetricType metricType : BlockMetricType.values()) {
273 if (!metricType.compactionAware && isCompaction) {
274 continue;
275 }
276
277 StringBuilder sb = new StringBuilder(metricPrefix);
278 if (blockCategory != BlockCategory.ALL_CATEGORIES
279 && blockCategory != BlockCategory.META) {
280 String categoryStr = blockCategory.toString();
281 categoryStr = categoryStr.charAt(0)
282 + categoryStr.substring(1).toLowerCase();
283 sb.append(BLOCK_TYPE_PREFIX + categoryStr + ".");
284 }
285
286 if (metricType.compactionAware) {
287 sb.append(isCompaction ? "compaction" : "fs");
288 }
289
290
291 if (blockCategory == BlockCategory.META) {
292 sb.append(META_BLOCK_CATEGORY_STR);
293 }
294
295 sb.append(metricType);
296
297 int i = getBlockMetricIndex(blockCategory, isCompaction, metricType);
298 blockMetricNames[i] = sb.toString();
299 blockMetricTimeVarying[i] = metricType.timeVarying;
300 }
301 }
302 }
303
304 for (boolean isInBloom : BOOL_VALUES) {
305 bloomMetricNames[isInBloom ? 1 : 0] = metricPrefix
306 + (isInBloom ? "keyMaybeInBloomCnt" : "keyNotInBloomCnt");
307 }
308
309 for (StoreMetricType storeMetric : StoreMetricType.values()) {
310 String coreName = metricPrefix + storeMetric.toString();
311 storeMetricNames[storeMetric.ordinal()] = coreName;
312 storeMetricNamesMax[storeMetric.ordinal()] = coreName + ".max";
313 }
314 }
315
316
317
318
319
320
321
322
323
324 public static SchemaMetrics getInstance(String tableName, String cfName) {
325 if (tableName == null) {
326 tableName = UNKNOWN;
327 }
328
329 if (cfName == null) {
330 cfName = UNKNOWN;
331 }
332
333 tableName = getEffectiveTableName(tableName);
334
335 final String instanceKey = tableName + "\t" + cfName;
336 SchemaMetrics schemaMetrics = tableAndFamilyToMetrics.get(instanceKey);
337 if (schemaMetrics != null) {
338 return schemaMetrics;
339 }
340
341 schemaMetrics = new SchemaMetrics(tableName, cfName);
342 SchemaMetrics existingMetrics =
343 tableAndFamilyToMetrics.putIfAbsent(instanceKey, schemaMetrics);
344 return existingMetrics != null ? existingMetrics : schemaMetrics;
345 }
346
347 private static final int getCacheHitMetricIndex (BlockCategory blockCategory,
348 boolean isCompaction) {
349 return blockCategory.ordinal() * BOOL_VALUES.length + (isCompaction ? 1 : 0);
350 }
351
352 private static final int getBlockMetricIndex(BlockCategory blockCategory,
353 boolean isCompaction, BlockMetricType metricType) {
354 int i = 0;
355 i = i * NUM_BLOCK_CATEGORIES + blockCategory.ordinal();
356 i = i * BOOL_VALUES.length + (isCompaction ? 1 : 0);
357 i = i * NUM_METRIC_TYPES + metricType.ordinal();
358 return i;
359 }
360
361 public String getBlockMetricName(BlockCategory blockCategory,
362 boolean isCompaction, BlockMetricType metricType) {
363 if (isCompaction && !metricType.compactionAware) {
364 throw new IllegalArgumentException("isCompaction cannot be true for "
365 + metricType);
366 }
367 return blockMetricNames[getBlockMetricIndex(blockCategory, isCompaction,
368 metricType)];
369 }
370
371 public String getBloomMetricName(boolean isInBloom) {
372 return bloomMetricNames[isInBloom ? 1 : 0];
373 }
374
375
376
377
378
379 private void incrNumericMetric(BlockCategory blockCategory,
380 boolean isCompaction, BlockMetricType metricType) {
381 incrNumericMetric (blockCategory, isCompaction, metricType, 1);
382 }
383
384
385
386
387
388 private void incrNumericMetric(BlockCategory blockCategory,
389 boolean isCompaction, BlockMetricType metricType, long amount) {
390 if (blockCategory == null) {
391 blockCategory = BlockCategory.UNKNOWN;
392 }
393 RegionMetricsStorage.incrNumericMetric(getBlockMetricName(blockCategory,
394 isCompaction, metricType), amount);
395
396 if (blockCategory != BlockCategory.ALL_CATEGORIES) {
397 incrNumericMetric(BlockCategory.ALL_CATEGORIES, isCompaction,
398 metricType, amount);
399 }
400 }
401
402 private void addToReadTime(BlockCategory blockCategory,
403 boolean isCompaction, long timeMs) {
404 RegionMetricsStorage.incrTimeVaryingMetric(getBlockMetricName(blockCategory,
405 isCompaction, BlockMetricType.READ_TIME), timeMs);
406
407
408 if (blockCategory != BlockCategory.ALL_CATEGORIES) {
409 addToReadTime(BlockCategory.ALL_CATEGORIES, isCompaction, timeMs);
410 }
411 }
412
413
414
415
416
417
418
419
420
421
422 public void accumulateStoreMetric(final Map<String, MutableDouble> tmpMap,
423 StoreMetricType storeMetricType, double val) {
424 final String key = getStoreMetricName(storeMetricType);
425 if (tmpMap.get(key) == null) {
426 tmpMap.put(key, new MutableDouble(val));
427 } else {
428 tmpMap.get(key).add(val);
429 }
430
431 if (this == ALL_SCHEMA_METRICS) {
432
433 final String maxKey = getStoreMetricNameMax(storeMetricType);
434 MutableDouble cur = tmpMap.get(maxKey);
435 if (cur == null) {
436 tmpMap.put(maxKey, new MutableDouble(val));
437 } else if (cur.doubleValue() < val) {
438 cur.setValue(val);
439 }
440 } else {
441 ALL_SCHEMA_METRICS.accumulateStoreMetric(tmpMap, storeMetricType, val);
442 }
443 }
444
445 public String getStoreMetricName(StoreMetricType storeMetricType) {
446 return storeMetricNames[storeMetricType.ordinal()];
447 }
448
449 public String getStoreMetricNameMax(StoreMetricType storeMetricType) {
450 return storeMetricNamesMax[storeMetricType.ordinal()];
451 }
452
453
454
455
456
457
458 public void updatePersistentStoreMetric(StoreMetricType storeMetricType,
459 long value) {
460 RegionMetricsStorage.incrNumericPersistentMetric(
461 storeMetricNames[storeMetricType.ordinal()], value);
462 }
463
464
465
466
467
468 public void updateOnCacheHit(BlockCategory blockCategory,
469 boolean isCompaction) {
470 updateOnCacheHit(blockCategory, isCompaction, 1);
471 }
472
473
474
475
476
477 public void updateOnCacheHit(BlockCategory blockCategory,
478 boolean isCompaction, long count) {
479 blockCategory.expectSpecific();
480 int idx = getCacheHitMetricIndex(blockCategory, isCompaction);
481
482 if (this.onHitCacheMetrics.addAndGet(idx, count) > THRESHOLD_METRICS_FLUSH) {
483 flushCertainOnCacheHitMetrics(blockCategory, isCompaction);
484 }
485
486 if (this != ALL_SCHEMA_METRICS) {
487 ALL_SCHEMA_METRICS.updateOnCacheHit(blockCategory, isCompaction, count);
488 }
489 }
490
491 private void flushCertainOnCacheHitMetrics(BlockCategory blockCategory, boolean isCompaction) {
492 int idx = getCacheHitMetricIndex(blockCategory, isCompaction);
493 long tempCount = this.onHitCacheMetrics.getAndSet(idx, 0);
494
495 if (tempCount > 0) {
496 incrNumericMetric(blockCategory, isCompaction, BlockMetricType.CACHE_HIT, tempCount);
497 incrNumericMetric(blockCategory, isCompaction, BlockMetricType.READ_COUNT, tempCount);
498 }
499 }
500
501
502
503
504 private void flushOnCacheHitMetrics() {
505 for (BlockCategory blockCategory : BlockCategory.values()) {
506 for (boolean isCompaction : BOOL_VALUES) {
507 flushCertainOnCacheHitMetrics (blockCategory, isCompaction);
508 }
509 }
510
511 if (this != ALL_SCHEMA_METRICS) {
512 ALL_SCHEMA_METRICS.flushOnCacheHitMetrics();
513 }
514 }
515
516
517
518
519 public void flushMetrics() {
520
521 flushOnCacheHitMetrics();
522 }
523
524
525
526
527
528 public void updateOnCacheMiss(BlockCategory blockCategory,
529 boolean isCompaction, long timeMs) {
530 blockCategory.expectSpecific();
531 addToReadTime(blockCategory, isCompaction, timeMs);
532 incrNumericMetric(blockCategory, isCompaction, BlockMetricType.CACHE_MISS);
533 incrNumericMetric(blockCategory, isCompaction, BlockMetricType.READ_COUNT);
534 if (this != ALL_SCHEMA_METRICS) {
535 ALL_SCHEMA_METRICS.updateOnCacheMiss(blockCategory, isCompaction,
536 timeMs);
537 }
538 }
539
540
541
542
543
544
545
546
547 public void addToCacheSize(BlockCategory category, long cacheSizeDelta) {
548 if (category == null) {
549 category = BlockCategory.ALL_CATEGORIES;
550 }
551 RegionMetricsStorage.incrNumericPersistentMetric(getBlockMetricName(category, false,
552 BlockMetricType.CACHE_SIZE), cacheSizeDelta);
553
554 if (category != BlockCategory.ALL_CATEGORIES) {
555 addToCacheSize(BlockCategory.ALL_CATEGORIES, cacheSizeDelta);
556 }
557 }
558
559 public void updateOnCachePutOrEvict(BlockCategory blockCategory,
560 long cacheSizeDelta, boolean isEviction) {
561 addToCacheSize(blockCategory, cacheSizeDelta);
562 incrNumericMetric(blockCategory, false,
563 isEviction ? BlockMetricType.EVICTED : BlockMetricType.CACHED);
564 if (this != ALL_SCHEMA_METRICS) {
565 ALL_SCHEMA_METRICS.updateOnCachePutOrEvict(blockCategory, cacheSizeDelta,
566 isEviction);
567 }
568 }
569
570
571
572
573
574 public void updateBloomMetrics(boolean isInBloom) {
575 RegionMetricsStorage.incrNumericMetric(getBloomMetricName(isInBloom), 1);
576 if (this != ALL_SCHEMA_METRICS) {
577 ALL_SCHEMA_METRICS.updateBloomMetrics(isInBloom);
578 }
579 }
580
581
582
583
584
585
586 public static void configureGlobally(Configuration conf) {
587 if (conf != null) {
588 final boolean useTableNameNew =
589 conf.getBoolean(SHOW_TABLE_NAME_CONF_KEY, true);
590 setUseTableName(useTableNameNew);
591 } else {
592 setUseTableName(true);
593 }
594 }
595
596
597
598
599
600
601
602
603
604
605
606 private static String getEffectiveTableName(String tableName) {
607 if (!tableName.equals(TOTAL_KEY)) {
608
609
610 if (useTableNameGlobally == null) {
611 throw new IllegalStateException("The value of the "
612 + SHOW_TABLE_NAME_CONF_KEY + " conf option has not been specified "
613 + "in SchemaMetrics");
614 }
615 final boolean useTableName = useTableNameGlobally;
616 if (!useTableName) {
617
618 tableName = TOTAL_KEY;
619 }
620 }
621 return tableName;
622 }
623
624
625
626
627
628
629
630
631
632
633 public static String generateSchemaMetricsPrefix(String tableName,
634 final String cfName) {
635 tableName = getEffectiveTableName(tableName);
636 String schemaMetricPrefix =
637 tableName.equals(TOTAL_KEY) ? "" : TABLE_PREFIX + tableName + ".";
638 schemaMetricPrefix +=
639 cfName.equals(TOTAL_KEY) ? "" : CF_PREFIX + cfName + ".";
640 return schemaMetricPrefix;
641 }
642
643 public static String generateSchemaMetricsPrefix(byte[] tableName,
644 byte[] cfName) {
645 return generateSchemaMetricsPrefix(Bytes.toString(tableName),
646 Bytes.toString(cfName));
647 }
648
649
650
651
652
653
654
655
656
657
658
659 public static String generateSchemaMetricsPrefix(String tableName,
660 Set<byte[]> families) {
661 if (families == null || families.isEmpty() ||
662 tableName == null || tableName.isEmpty()) {
663 return "";
664 }
665
666 if (families.size() == 1) {
667 return generateSchemaMetricsPrefix(tableName,
668 Bytes.toString(families.iterator().next()));
669 }
670
671 tableName = getEffectiveTableName(tableName);
672 List<byte[]> sortedFamilies = new ArrayList<byte[]>(families);
673 Collections.sort(sortedFamilies, Bytes.BYTES_COMPARATOR);
674
675 StringBuilder sb = new StringBuilder();
676
677 int numCFsLeft = families.size();
678 for (byte[] family : sortedFamilies) {
679 if (sb.length() > MAX_METRIC_PREFIX_LENGTH) {
680 sb.append(MORE_CFS_OMITTED_STR);
681 break;
682 }
683 --numCFsLeft;
684 sb.append(Bytes.toString(family));
685 if (numCFsLeft > 0) {
686 sb.append("~");
687 }
688 }
689
690 return SchemaMetrics.generateSchemaMetricsPrefix(tableName, sb.toString());
691 }
692
693
694
695
696
697
698
699
700
701
702 static String generateRegionMetricsPrefix(String tableName, String regionName) {
703 tableName = getEffectiveTableName(tableName);
704 String schemaMetricPrefix = tableName.equals(TOTAL_KEY) ? "" : TABLE_PREFIX + tableName + ".";
705 schemaMetricPrefix += regionName.equals(TOTAL_KEY) ? "" : REGION_PREFIX + regionName + ".";
706
707 return schemaMetricPrefix;
708 }
709
710
711
712
713
714
715 private static void setUseTableName(final boolean useTableNameNew) {
716 if (useTableNameGlobally == null) {
717
718 useTableNameGlobally = useTableNameNew;
719 } else if (useTableNameGlobally != useTableNameNew
720 && !loggedConfInconsistency) {
721
722
723 LOG.error("Inconsistent configuration. Previous configuration "
724 + "for using table name in metrics: " + useTableNameGlobally + ", "
725 + "new configuration: " + useTableNameNew);
726 loggedConfInconsistency = true;
727 }
728 }
729
730
731
732 private static final String regexEscape(String s) {
733 return s.replace(".", "\\.");
734 }
735
736
737
738
739
740 private static final String WORD_AND_DOT_RE_STR = "([^.]+|" +
741 regexEscape(Bytes.toString(HConstants.META_TABLE_NAME)) +
742 ")\\.";
743
744
745 private static final String TABLE_NAME_RE_STR =
746 "\\b" + regexEscape(TABLE_PREFIX) + WORD_AND_DOT_RE_STR;
747
748
749 private static final String CF_NAME_RE_STR =
750 "\\b" + regexEscape(CF_PREFIX) + WORD_AND_DOT_RE_STR;
751 private static final Pattern CF_NAME_RE = Pattern.compile(CF_NAME_RE_STR);
752
753
754 private static final Pattern TABLE_AND_CF_NAME_RE = Pattern.compile(
755 TABLE_NAME_RE_STR + CF_NAME_RE_STR);
756
757 private static final Pattern BLOCK_CATEGORY_RE = Pattern.compile(
758 "\\b" + regexEscape(BLOCK_TYPE_PREFIX) + "[^.]+\\." +
759
760 "|" + META_BLOCK_CATEGORY_STR + "(?=" +
761 BlockMetricType.BLOCK_METRIC_TYPE_RE + ")");
762
763
764
765
766
767
768 private static String NUM_OPS_SUFFIX = "numops";
769
770
771
772
773
774 private static String TOTAL_SUFFIX = "_total";
775 private static final Pattern TIME_VARYING_SUFFIX_RE = Pattern.compile(
776 "(" + NUM_OPS_SUFFIX + "|" + TOTAL_SUFFIX + ")$");
777
778 void printMetricNames() {
779 for (BlockCategory blockCategory : BlockCategory.values()) {
780 for (boolean isCompaction : BOOL_VALUES) {
781 for (BlockMetricType metricType : BlockMetricType.values()) {
782 int i = getBlockMetricIndex(blockCategory, isCompaction, metricType);
783 LOG.debug("blockCategory=" + blockCategory + ", "
784 + "metricType=" + metricType + ", isCompaction=" + isCompaction
785 + ", metricName=" + blockMetricNames[i]);
786 }
787 }
788 }
789 }
790
791 private Collection<String> getAllMetricNames() {
792 List<String> allMetricNames = new ArrayList<String>();
793 for (int i = 0; i < blockMetricNames.length; ++i) {
794 final String blockMetricName = blockMetricNames[i];
795 final boolean timeVarying = blockMetricTimeVarying[i];
796 if (blockMetricName != null) {
797 if (timeVarying) {
798 allMetricNames.add(blockMetricName + NUM_OPS_SUFFIX);
799 allMetricNames.add(blockMetricName + TOTAL_SUFFIX);
800 } else {
801 allMetricNames.add(blockMetricName);
802 }
803 }
804 }
805 allMetricNames.addAll(Arrays.asList(bloomMetricNames));
806 return allMetricNames;
807 }
808
809 private static final boolean isTimeVaryingKey(String metricKey) {
810 return metricKey.endsWith(NUM_OPS_SUFFIX)
811 || metricKey.endsWith(TOTAL_SUFFIX);
812 }
813
814 private static final String stripTimeVaryingSuffix(String metricKey) {
815 return TIME_VARYING_SUFFIX_RE.matcher(metricKey).replaceAll("");
816 }
817
818 public static Map<String, Long> getMetricsSnapshot() {
819 Map<String, Long> metricsSnapshot = new TreeMap<String, Long>();
820 for (SchemaMetrics cfm : tableAndFamilyToMetrics.values()) {
821 cfm.flushMetrics();
822 for (String metricName : cfm.getAllMetricNames()) {
823 long metricValue;
824 if (isTimeVaryingKey(metricName)) {
825 Pair<Long, Integer> totalAndCount =
826 RegionMetricsStorage.getTimeVaryingMetric(stripTimeVaryingSuffix(metricName));
827 metricValue = metricName.endsWith(TOTAL_SUFFIX) ?
828 totalAndCount.getFirst() : totalAndCount.getSecond();
829 } else {
830 metricValue = RegionMetricsStorage.getNumericMetric(metricName);
831 }
832
833 metricsSnapshot.put(metricName, metricValue);
834 }
835 }
836 return metricsSnapshot;
837 }
838
839 public static long getLong(Map<String, Long> m, String k) {
840 Long l = m.get(k);
841 return l != null ? l : 0;
842 }
843
844 private static void putLong(Map<String, Long> m, String k, long v) {
845 if (v != 0) {
846 m.put(k, v);
847 } else {
848 m.remove(k);
849 }
850 }
851
852
853
854
855
856 public static Map<String, Long> diffMetrics(Map<String, Long> a,
857 Map<String, Long> b) {
858 Set<String> allKeys = new TreeSet<String>(a.keySet());
859 allKeys.addAll(b.keySet());
860 Map<String, Long> diff = new TreeMap<String, Long>();
861 for (String k : allKeys) {
862 long aVal = getLong(a, k);
863 long bVal = getLong(b, k);
864 if (aVal != bVal) {
865 diff.put(k, bVal - aVal);
866 }
867 }
868 return diff;
869 }
870
871 public static void validateMetricChanges(Map<String, Long> oldMetrics) {
872 final Map<String, Long> newMetrics = getMetricsSnapshot();
873 final Map<String, Long> allCfDeltas = new TreeMap<String, Long>();
874 final Map<String, Long> allBlockCategoryDeltas =
875 new TreeMap<String, Long>();
876 final Map<String, Long> deltas = diffMetrics(oldMetrics, newMetrics);
877 final Pattern cfTableMetricRE =
878 useTableNameGlobally ? TABLE_AND_CF_NAME_RE : CF_NAME_RE;
879 final Set<String> allKeys = new TreeSet<String>(oldMetrics.keySet());
880 allKeys.addAll(newMetrics.keySet());
881
882 for (SchemaMetrics cfm : tableAndFamilyToMetrics.values()) {
883 for (String metricName : cfm.getAllMetricNames()) {
884 if (metricName.startsWith(CF_PREFIX + CF_PREFIX)) {
885 throw new AssertionError("Column family prefix used twice: " +
886 metricName);
887 }
888
889 final long oldValue = getLong(oldMetrics, metricName);
890 final long newValue = getLong(newMetrics, metricName);
891 final long delta = newValue - oldValue;
892
893
894
895 if (delta != 0) {
896 if (cfm != ALL_SCHEMA_METRICS) {
897 final String aggregateMetricName =
898 cfTableMetricRE.matcher(metricName).replaceAll("");
899 if (!aggregateMetricName.equals(metricName)) {
900 LOG.debug("Counting " + delta + " units of " + metricName
901 + " towards " + aggregateMetricName);
902
903 putLong(allCfDeltas, aggregateMetricName,
904 getLong(allCfDeltas, aggregateMetricName) + delta);
905 }
906 } else {
907 LOG.debug("Metric=" + metricName + ", delta=" + delta);
908 }
909 }
910
911 Matcher matcher = BLOCK_CATEGORY_RE.matcher(metricName);
912 if (matcher.find()) {
913
914 String metricNoBlockCategory = matcher.replaceAll("");
915
916 putLong(allBlockCategoryDeltas, metricNoBlockCategory,
917 getLong(allBlockCategoryDeltas, metricNoBlockCategory) + delta);
918 }
919 }
920 }
921
922 StringBuilder errors = new StringBuilder();
923 for (String key : ALL_SCHEMA_METRICS.getAllMetricNames()) {
924 long actual = getLong(deltas, key);
925 long expected = getLong(allCfDeltas, key);
926 if (actual != expected) {
927 if (errors.length() > 0)
928 errors.append("\n");
929 errors.append("The all-CF metric " + key + " changed by "
930 + actual + " but the aggregation of per-CF/table metrics "
931 + "yields " + expected);
932 }
933 }
934
935
936
937 for (String key : allKeys) {
938 if (BLOCK_CATEGORY_RE.matcher(key).find() ||
939 key.contains(ALL_SCHEMA_METRICS.getBloomMetricName(false)) ||
940 key.contains(ALL_SCHEMA_METRICS.getBloomMetricName(true))){
941
942
943 continue;
944 }
945 long actual = getLong(deltas, key);
946 long expected = getLong(allBlockCategoryDeltas, key);
947 if (actual != expected) {
948 if (errors.length() > 0)
949 errors.append("\n");
950 errors.append("The all-block-category metric " + key
951 + " changed by " + actual + " but the aggregation of "
952 + "per-block-category metrics yields " + expected);
953 }
954 }
955
956 if (errors.length() > 0) {
957 throw new AssertionError(errors.toString());
958 }
959 }
960
961
962
963
964
965 public static SchemaMetrics getUnknownInstanceForTest() {
966 return getInstance(UNKNOWN, UNKNOWN);
967 }
968
969
970
971
972
973 public static void setUseTableNameInTest(final boolean useTableNameNew) {
974 useTableNameGlobally = useTableNameNew;
975 }
976
977
978 public static String formatMetrics(Map<String, Long> metrics) {
979 StringBuilder sb = new StringBuilder();
980 for (Map.Entry<String, Long> entry : metrics.entrySet()) {
981 if (sb.length() > 0) {
982 sb.append('\n');
983 }
984 sb.append(entry.getKey() + " : " + entry.getValue());
985 }
986 return sb.toString();
987 }
988
989 }