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;
25  import java.util.Map.Entry;
26  import java.util.concurrent.atomic.AtomicLong;
27  import java.util.concurrent.locks.ReadWriteLock;
28  import java.util.concurrent.locks.ReentrantReadWriteLock;
29  
30  import org.apache.hadoop.hbase.util.Pair;
31  import org.apache.hadoop.metrics.MetricsRecord;
32  import org.apache.hadoop.metrics.util.MetricsBase;
33  import org.apache.hadoop.metrics.util.MetricsRegistry;
34  import org.cliffc.high_scale_lib.Counter;
35  
36  import com.google.common.base.Function;
37  import com.google.common.collect.Lists;
38  import com.google.common.collect.MapMaker;
39  
40  public class ExactCounterMetric extends MetricsBase {
41  
42    private static final int DEFAULT_TOP_N = 5;
43  
44    // only publish stats on the topN items (default to DEFAULT_TOP_N)
45    private final int topN;
46    private final Map<String, Counter> counts;
47  
48    // all access to the 'counts' map should use this lock.
49    // take a write lock iff you want to guarantee exclusive access
50    // (the map stripes locks internally, so it's already thread safe -
51    // this lock is just so you can take a consistent snapshot of data)
52    private final ReadWriteLock lock;
53    
54    
55    /**
56     * Constructor to create a new counter metric
57     * @param nam         the name to publish this metric under
58     * @param registry    where the metrics object will be registered
59     * @param description metrics description
60     * @param topN        how many 'keys' to publish metrics on 
61     */
62    public ExactCounterMetric(final String nam, final MetricsRegistry registry, 
63        final String description, int topN) {
64      super(nam, description);
65  
66      this.counts = new MapMaker().makeComputingMap(
67          new Function<String, Counter>() {
68            @Override
69            public Counter apply(String input) {
70              return new Counter();
71            }    
72          });
73  
74      this.lock = new ReentrantReadWriteLock();
75      this.topN = topN;
76  
77      if (registry != null) {
78        registry.add(nam, this);      
79      }
80    }
81  
82    /**
83     * Constructor creates a new ExactCounterMetric
84     * @param nam       the name of the metrics to be used to publish the metric
85     * @param registry  where the metrics object will be registered
86     */
87    public ExactCounterMetric(final String nam, MetricsRegistry registry) {
88      this(nam, registry, NO_DESCRIPTION, DEFAULT_TOP_N);
89    }
90  
91    
92    public void update(String type) {
93      this.lock.readLock().lock();
94      try {
95        this.counts.get(type).increment();
96      } finally {
97        this.lock.readLock().unlock();
98      }
99    }
100   
101   public void update(String type, long count) {
102     this.lock.readLock().lock();
103     try {
104       this.counts.get(type).add(count);
105     } finally {
106       this.lock.readLock().unlock();
107     }
108   }
109   
110   public List<Pair<String, Long>> getTop(int n) {
111     final List<Pair<String, Long>> countsSnapshot = 
112         Lists.newArrayListWithCapacity(this.counts.size());
113     
114     // no updates are allowed while I'm holding this lock, so move fast
115     this.lock.writeLock().lock();
116     try {
117       for(Entry<String, Counter> entry : this.counts.entrySet()) {
118         countsSnapshot.add(Pair.newPair(entry.getKey(), 
119             entry.getValue().get()));
120       }
121     } finally {
122       this.lock.writeLock().unlock();
123     }
124     
125     Collections.sort(countsSnapshot, new Comparator<Pair<String, Long>>() {
126       @Override
127       public int compare(Pair<String, Long> a, Pair<String, Long> b) {
128         return b.getSecond().compareTo(a.getSecond());
129       }      
130     });
131     
132     return countsSnapshot.subList(0, Math.min(n, countsSnapshot.size()));
133   }
134   
135   @Override
136   public void pushMetric(MetricsRecord mr) {
137     final List<Pair<String, Long>> topKeys = getTop(Integer.MAX_VALUE);
138     int sum = 0;
139     
140     int counter = 0;
141     for (Pair<String, Long> keyCount : topKeys) {
142       counter++;
143       // only push stats on the topN keys
144       if (counter <= this.topN) {
145         mr.setMetric(getName() + "_" + keyCount.getFirst(), 
146             keyCount.getSecond());        
147       }
148       sum += keyCount.getSecond();
149     }
150     mr.setMetric(getName() + "_map_size", this.counts.size());
151     mr.setMetric(getName() + "_total_count", sum);
152   }
153   
154 }