Added per socket monitoring for client executor pool. These monitoring reveals stats...
[voldemort/jeffpc.git] / src / java / voldemort / store / stats / ClientSocketStats.java
blob048c340b1b7c2f83d55dac6469514f1201765fa0
1 /*
2 * Copyright 2008-2011 LinkedIn, Inc
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
17 package voldemort.store.stats;
19 import java.util.Iterator;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.ConcurrentMap;
22 import java.util.concurrent.atomic.AtomicInteger;
23 import java.util.concurrent.atomic.AtomicLong;
25 import voldemort.store.socket.SocketDestination;
26 import voldemort.store.socket.clientrequest.ClientRequestExecutor;
27 import voldemort.utils.JmxUtils;
28 import voldemort.utils.pool.KeyedResourcePool;
30 /**
31 * Some convenient statistics to track about the client requests
35 public class ClientSocketStats {
37 private final ClientSocketStats parent;
38 private final ConcurrentMap<SocketDestination, ClientSocketStats> statsMap;
39 private final SocketDestination destination;
40 private final KeyedResourcePool<SocketDestination, ClientRequestExecutor> pool;
42 private final AtomicInteger monitoringInterval = new AtomicInteger(10000);
43 private final Histogram checkoutTimeUsHistogram = new Histogram(20000, 100);
44 private final AtomicLong totalCheckoutTimeUs = new AtomicLong(0);
45 private final AtomicLong avgCheckoutTimeUs = new AtomicLong(0);
46 private final AtomicInteger connectionsCreated = new AtomicInteger(0);
47 private final AtomicInteger connectionsDestroyed = new AtomicInteger(0);
48 private final AtomicInteger connectionsCheckedout = new AtomicInteger(0);
49 private boolean jmxEnable = false;
51 // private final AtomicInteger connectionsCheckedin = new AtomicInteger(0);
53 /**
54 * To construct a per node stats object
56 * @param parent An optional parent stats object that will maintain
57 * aggregate data across many sockets
58 * @param destination The destination object that defines the node
59 * @param pool The socket pool that will give out connection information
61 public ClientSocketStats(ClientSocketStats parent,
62 SocketDestination destination,
63 KeyedResourcePool<SocketDestination, ClientRequestExecutor> pool) {
64 this.parent = parent;
65 this.statsMap = null;
66 this.destination = destination;
67 this.pool = pool;
70 /**
71 * Construction of a new aggregate stats object
73 * @param pool The socket pool that will give out connection information
75 public ClientSocketStats(KeyedResourcePool<SocketDestination, ClientRequestExecutor> pool) {
76 this.parent = null;
77 this.statsMap = new ConcurrentHashMap<SocketDestination, ClientSocketStats>();
78 this.destination = null;
79 this.pool = pool;
82 /* get per node stats, create one if not exist */
83 private ClientSocketStats getOrCreateNodeStats(SocketDestination destination) {
84 if(destination == null) {
85 return null;
87 ClientSocketStats stats = statsMap.get(destination);
88 if(stats == null) {
89 stats = new ClientSocketStats(this, destination, pool);
90 statsMap.putIfAbsent(destination, stats);
91 stats = statsMap.get(destination);
92 if(this.jmxEnable) {
93 JmxUtils.registerMbean(new ClientSocketStatsJmx(stats),
94 JmxUtils.createObjectName("voldemort.store.socket.clientrequest",
95 "stats_"
96 + destination.toString()
97 .replace(':',
98 '_')));
101 return stats;
105 * Record the checkout wait time in us
107 * @param dest Destination of the socket to checkout. Will actually record
108 * if null. Otherwise will call this on self and corresponding child
109 * with this param null.
110 * @param checkoutTimeUs The number of us to wait before getting a socket
112 public void recordCheckoutTimeUs(SocketDestination dest, long checkoutTimeUs) {
113 if(dest != null) {
114 getOrCreateNodeStats(dest).recordCheckoutTimeUs(null, checkoutTimeUs);
115 recordCheckoutTimeUs(null, checkoutTimeUs);
116 } else {
117 this.totalCheckoutTimeUs.getAndAdd(checkoutTimeUs);
118 checkoutTimeUsHistogram.insert(checkoutTimeUs);
119 int checkouts = this.connectionsCheckedout.getAndIncrement();
121 // reset aggregated stats and all the node stats for new interval
122 if(parent == null && statsMap != null) {
123 int interval = this.monitoringInterval.get();
124 if(checkouts % interval == interval - 1) {
125 // reset all children
126 Iterator<SocketDestination> it = statsMap.keySet().iterator();
127 while(it.hasNext()) {
128 ClientSocketStats stats = statsMap.get(it.next());
129 stats.resetForInterval();
131 // reset itself
132 resetForInterval();
139 * Calculate the average and reset the stats
141 public void resetForInterval() {
142 // harmless race condition:
143 this.totalCheckoutTimeUs.set(0);
144 this.connectionsCheckedout.set(0);
145 checkoutTimeUsHistogram.reset();
148 public void connectionCreate(SocketDestination dest) {
149 if(dest != null) {
150 getOrCreateNodeStats(dest).connectionCreate(null);
151 connectionCreate(null);
152 } else {
153 this.connectionsCreated.getAndIncrement();
157 public void connectionDestroy(SocketDestination dest) {
158 if(dest != null) {
159 getOrCreateNodeStats(dest).connectionDestroy(null);
160 connectionDestroy(null);
161 } else {
162 this.connectionsDestroyed.getAndIncrement();
166 /* getters */
168 public int getConnectionsCreated() {
169 return connectionsCreated.intValue();
172 public int getConnectionsDestroyed() {
173 return connectionsDestroyed.intValue();
176 // public int getConnectionsCheckedin() {
177 // return connectionsCheckedin.intValue();
178 // }
180 public int getConnectionsCheckedout() {
181 return connectionsCheckedout.intValue();
184 public Histogram getWaitHistogram() {
185 return this.checkoutTimeUsHistogram;
188 public long getAveWaitUs() {
189 long ns = totalCheckoutTimeUs.get();
190 int count = connectionsCheckedout.get();
191 this.avgCheckoutTimeUs.set(count > 0 ? (ns / count) : -1);
192 return this.avgCheckoutTimeUs.longValue();
195 public int getConnectionsActive(SocketDestination destination) {
196 if(destination == null) {
197 return pool.getTotalResourceCount();
198 } else {
199 return pool.getTotalResourceCount(destination);
203 public int getConnectionsInPool(SocketDestination destination) {
204 if(destination == null) {
205 return pool.getCheckedInResourceCount();
206 } else {
207 return pool.getCheckedInResourcesCount(destination);
211 public void setMonitoringInterval(int count) {
212 this.monitoringInterval.set(count);
215 public int getMonitoringInterval() {
216 return this.monitoringInterval.get();
219 public void enableJmx() {
220 jmxEnable = true;
223 ConcurrentMap<SocketDestination, ClientSocketStats> getStatsMap() {
224 return statsMap;
227 SocketDestination getDestination() {
228 return destination;