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 */ 018 019package org.apache.hadoop.metrics2.lib; 020 021import org.apache.hadoop.classification.InterfaceAudience; 022import org.apache.hadoop.classification.InterfaceStability; 023import org.apache.hadoop.metrics2.MetricsInfo; 024import org.apache.hadoop.metrics2.MetricsTag; 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028import java.util.LinkedHashMap; 029import java.util.Map; 030 031/** 032 * Helpers to create interned metrics info. 033 */ 034@InterfaceAudience.Public 035@InterfaceStability.Evolving 036public class Interns { 037 private static final Logger LOG = LoggerFactory.getLogger(Interns.class); 038 039 // A simple intern cache with two keys 040 // (to avoid creating new (combined) key objects for lookup) 041 private static abstract class CacheWith2Keys<K1, K2, V> { 042 private final Map<K1, Map<K2, V>> k1Map = 043 new LinkedHashMap<K1, Map<K2, V>>() { 044 private static final long serialVersionUID = 1L; 045 private boolean gotOverflow = false; 046 @Override 047 protected boolean removeEldestEntry(Map.Entry<K1, Map<K2, V>> e) { 048 boolean overflow = expireKey1At(size()); 049 if (overflow && !gotOverflow) { 050 LOG.info("Metrics intern cache overflow at {} for {}", size(), e); 051 gotOverflow = true; 052 } 053 return overflow; 054 } 055 }; 056 057 abstract protected boolean expireKey1At(int size); 058 abstract protected boolean expireKey2At(int size); 059 abstract protected V newValue(K1 k1, K2 k2); 060 061 synchronized V add(K1 k1, K2 k2) { 062 Map<K2, V> k2Map = k1Map.get(k1); 063 if (k2Map == null) { 064 k2Map = new LinkedHashMap<K2, V>() { 065 private static final long serialVersionUID = 1L; 066 private boolean gotOverflow = false; 067 @Override protected boolean removeEldestEntry(Map.Entry<K2, V> e) { 068 boolean overflow = expireKey2At(size()); 069 if (overflow && !gotOverflow) { 070 LOG.info("Metrics intern cache overflow at {} for {}", size(), e); 071 gotOverflow = true; 072 } 073 return overflow; 074 } 075 }; 076 k1Map.put(k1, k2Map); 077 } 078 V v = k2Map.get(k2); 079 if (v == null) { 080 v = newValue(k1, k2); 081 k2Map.put(k2, v); 082 } 083 return v; 084 } 085 } 086 087 // Sanity limits in case of misuse/abuse. 088 static final int MAX_INFO_NAMES = 2010; 089 static final int MAX_INFO_DESCS = 100; // distinct per name 090 091 enum Info { 092 INSTANCE; 093 094 final CacheWith2Keys<String, String, MetricsInfo> cache = 095 new CacheWith2Keys<String, String, MetricsInfo>() { 096 097 @Override protected boolean expireKey1At(int size) { 098 return size > MAX_INFO_NAMES; 099 } 100 101 @Override protected boolean expireKey2At(int size) { 102 return size > MAX_INFO_DESCS; 103 } 104 105 @Override protected MetricsInfo newValue(String name, String desc) { 106 return new MetricsInfoImpl(name, desc); 107 } 108 }; 109 } 110 111 /** 112 * Get a metric info object. 113 * @param name Name of metric info object 114 * @param description Description of metric info object 115 * @return an interned metric info object 116 */ 117 public static MetricsInfo info(String name, String description) { 118 return Info.INSTANCE.cache.add(name, description); 119 } 120 121 // Sanity limits 122 static final int MAX_TAG_NAMES = 100; 123 static final int MAX_TAG_VALUES = 1000; // distinct per name 124 125 enum Tags { 126 INSTANCE; 127 128 final CacheWith2Keys<MetricsInfo, String, MetricsTag> cache = 129 new CacheWith2Keys<MetricsInfo, String, MetricsTag>() { 130 131 @Override protected boolean expireKey1At(int size) { 132 return size > MAX_TAG_NAMES; 133 } 134 135 @Override protected boolean expireKey2At(int size) { 136 return size > MAX_TAG_VALUES; 137 } 138 139 @Override protected MetricsTag newValue(MetricsInfo info, String value) { 140 return new MetricsTag(info, value); 141 } 142 }; 143 } 144 145 /** 146 * Get a metrics tag. 147 * @param info of the tag 148 * @param value of the tag 149 * @return an interned metrics tag 150 */ 151 public static MetricsTag tag(MetricsInfo info, String value) { 152 return Tags.INSTANCE.cache.add(info, value); 153 } 154 155 /** 156 * Get a metrics tag. 157 * @param name of the tag 158 * @param description of the tag 159 * @param value of the tag 160 * @return an interned metrics tag 161 */ 162 public static MetricsTag tag(String name, String description, String value) { 163 return Tags.INSTANCE.cache.add(info(name, description), value); 164 } 165}