View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.metrics;
20  
21  import java.util.Collections;
22  import java.util.Comparator;
23  import java.util.List;
24  import java.util.Map.Entry;
25  import java.util.concurrent.ConcurrentHashMap;
26  import java.util.concurrent.ConcurrentMap;
27  import java.util.concurrent.locks.ReadWriteLock;
28  import java.util.concurrent.locks.ReentrantReadWriteLock;
29  
30  import org.apache.hadoop.hbase.util.Counter;
31  import org.apache.hadoop.hbase.util.Pair;
32  import org.apache.hadoop.metrics.MetricsRecord;
33  import org.apache.hadoop.metrics.util.MetricsBase;
34  import org.apache.hadoop.metrics.util.MetricsRegistry;
35  
36  import com.google.common.collect.Lists;
37  
38  @Deprecated
39  public class ExactCounterMetric extends MetricsBase {
40  
41    private static final int DEFAULT_TOP_N = 5;
42  
43    // only publish stats on the topN items (default to DEFAULT_TOP_N)
44    private final int topN;
45    private final ConcurrentMap<String, Counter> counts = new ConcurrentHashMap<String, Counter>();
46  
47    // all access to the 'counts' map should use this lock.
48    // take a write lock iff you want to guarantee exclusive access
49    // (the map stripes locks internally, so it's already thread safe -
50    // this lock is just so you can take a consistent snapshot of data)
51    private final ReadWriteLock lock;
52  
53      /**
54       * Constructor to create a new counter metric
55       * @param nam         the name to publish this metric under
56       * @param registry    where the metrics object will be registered
57       * @param description metrics description
58       * @param topN        how many 'keys' to publish metrics on
59       */
60    public ExactCounterMetric(final String nam, final MetricsRegistry registry, 
61        final String description, int topN) {
62      super(nam, description);
63  
64      this.lock = new ReentrantReadWriteLock();
65      this.topN = topN;
66  
67      if (registry != null) {
68        registry.add(nam, this);      
69      }
70    }
71  
72    /**
73     * Constructor creates a new ExactCounterMetric
74     * @param nam       the name of the metrics to be used to publish the metric
75     * @param registry  where the metrics object will be registered
76     */
77    public ExactCounterMetric(final String nam, MetricsRegistry registry) {
78      this(nam, registry, NO_DESCRIPTION, DEFAULT_TOP_N);
79    }
80  
81    /**
82     * Relies on an external lock on {@link #lock} for thread safety.
83     */
84    private Counter getOrCreateCounter(String type){
85      Counter cnt = counts.get(type);
86      if (cnt == null){
87        cnt = new Counter();
88        counts.put(type, cnt);
89      }
90      return cnt;
91    }
92  
93    public void update(String type) {
94      this.lock.readLock().lock();
95      try {
96        getOrCreateCounter(type).increment();
97      } finally {
98        this.lock.readLock().unlock();
99      }
100   }
101   
102   public void update(String type, long count) {
103     this.lock.readLock().lock();
104     try {
105       getOrCreateCounter(type).add(count);
106     } finally {
107       this.lock.readLock().unlock();
108     }
109   }
110   
111   public List<Pair<String, Long>> getTop(int n) {
112     final List<Pair<String, Long>> countsSnapshot = 
113         Lists.newArrayListWithCapacity(this.counts.size());
114     
115     // no updates are allowed while I'm holding this lock, so move fast
116     this.lock.writeLock().lock();
117     try {
118       for(Entry<String, Counter> entry : this.counts.entrySet()) {
119         countsSnapshot.add(Pair.newPair(entry.getKey(), 
120             entry.getValue().get()));
121       }
122     } finally {
123       this.lock.writeLock().unlock();
124     }
125     
126     Collections.sort(countsSnapshot, new Comparator<Pair<String, Long>>() {
127       @Override
128       public int compare(Pair<String, Long> a, Pair<String, Long> b) {
129         return b.getSecond().compareTo(a.getSecond());
130       }      
131     });
132     
133     return countsSnapshot.subList(0, Math.min(n, countsSnapshot.size()));
134   }
135   
136   @Override
137   public void pushMetric(MetricsRecord mr) {
138     final List<Pair<String, Long>> topKeys = getTop(Integer.MAX_VALUE);
139     int sum = 0;
140     
141     int counter = 0;
142     for (Pair<String, Long> keyCount : topKeys) {
143       counter++;
144       // only push stats on the topN keys
145       if (counter <= this.topN) {
146         mr.setMetric(getName() + "_" + keyCount.getFirst(), 
147             keyCount.getSecond());        
148       }
149       sum += keyCount.getSecond();
150     }
151     mr.setMetric(getName() + "_map_size", this.counts.size());
152     mr.setMetric(getName() + "_total_count", sum);
153   }
154   
155 }