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    
019    package org.apache.hadoop.metrics2.lib;
020    
021    import java.util.Map;
022    import java.util.LinkedHashMap;
023    
024    import org.apache.commons.logging.Log;
025    import org.apache.commons.logging.LogFactory;
026    import org.apache.hadoop.classification.InterfaceAudience;
027    import org.apache.hadoop.classification.InterfaceStability;
028    import org.apache.hadoop.metrics2.MetricsInfo;
029    import org.apache.hadoop.metrics2.MetricsTag;
030    
031    /**
032     * Helpers to create interned metrics info
033     */
034    @InterfaceAudience.Public
035    @InterfaceStability.Evolving
036    public class Interns {
037      private static final Log LOG = LogFactory.getLog(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.warn("Metrics intern cache overflow at "+ size() +" for "+ 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.warn("Metrics intern cache overflow at "+ size() +" for "+ 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
114       * @param description
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    }