1 /**
2 * Copyright Terracotta, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package net.sf.ehcache.management;
18
19 import net.sf.ehcache.CacheException;
20 import net.sf.ehcache.Ehcache;
21 import net.sf.ehcache.Statistics;
22 import net.sf.ehcache.hibernate.management.impl.EhcacheHibernateMbeanNames;
23
24 import javax.management.MalformedObjectNameException;
25 import javax.management.ObjectName;
26
27 import java.io.Serializable;
28 import java.util.concurrent.TimeUnit;
29
30
31 /**
32 * A JMX CacheStatistics decorator for an ehcache Statistics class.
33 * <p/>
34 * An immutable Cache statistics implementation}
35 * <p/>
36 * This is like a value object, with the added ability to clear cache statistics on the cache.
37 * That ability does not survive any Serialization of this class. On deserialization the cache
38 * can be considered disconnected.
39 * <p/>
40 * The accuracy of these statistics are determined by the value of {#getStatisticsAccuracy()}
41 * at the time the statistic was computed. This can be changed by setting {@link net.sf.ehcache.Cache#setStatisticsAccuracy}.
42 * <p/>
43 * Because this class maintains a reference to an Ehcache, any references held to this class will precent the Ehcache
44 * from getting garbage collected.
45 *
46 * @author Greg Luck
47 * @version $Id: CacheStatistics.java 5631 2012-05-10 08:31:33Z teck $
48 * @since 1.3
49 */
50 public class CacheStatistics implements CacheStatisticsMBean, Serializable {
51
52 private static final long serialVersionUID = 8085302752781762030L;
53
54 private final transient Ehcache ehcache;
55 private final ObjectName objectName;
56 private final long ttlInMillis;
57
58 private Statistics statistics;
59 private long lastUpdated;
60
61 /**
62 * Constructs an object from an ehcache statistics object
63 *
64 * @param ehcache the backing ehcache
65 * @param ttl statistics ttl value
66 * @param unit statistics ttl unit
67 */
68 public CacheStatistics(Ehcache ehcache, long ttl, TimeUnit unit) {
69 this.ttlInMillis = unit.toMillis(ttl);
70 this.ehcache = ehcache;
71 this.objectName = createObjectName(ehcache.getCacheManager().getName(), ehcache.getName());
72 }
73
74 /**
75 * Constructs an object from an ehcache statistics object using the default statistics ttl.
76 *
77 * @param ehcache the backing ehcache
78 */
79 public CacheStatistics(Ehcache ehcache) {
80 this(ehcache, 0, TimeUnit.MILLISECONDS);
81 }
82
83 /**
84 * Creates an object name using the scheme "net.sf.ehcache:type=CacheStatistics,CacheManager=<cacheManagerName>,name=<cacheName>"
85 */
86 static ObjectName createObjectName(String cacheManagerName, String cacheName) {
87 ObjectName objectName;
88 try {
89 objectName = new ObjectName("net.sf.ehcache:type=CacheStatistics,CacheManager=" + cacheManagerName + ",name="
90 + EhcacheHibernateMbeanNames.mbeanSafe(cacheName));
91 } catch (MalformedObjectNameException e) {
92 throw new CacheException(e);
93 }
94 return objectName;
95 }
96
97 /**
98 * Accurately measuring statistics can be expensive. Returns the current accuracy setting used
99 * in the creation of these statistics.
100 *
101 * @return one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
102 */
103 public int getStatisticsAccuracy() {
104 updateIfNeeded();
105 return statistics.getStatisticsAccuracy();
106 }
107
108 private void updateIfNeeded() {
109 long statsAge = System.currentTimeMillis() - lastUpdated;
110 if (statsAge < 0 || statsAge > ttlInMillis) {
111 statistics = ehcache.getStatistics();
112 lastUpdated = System.currentTimeMillis();
113 }
114 }
115
116 /**
117 * Accurately measuring statistics can be expensive. Returns the current accuracy setting.
118 *
119 * @return a human readable description of the accuracy setting. One of "None", "Best Effort" or "Guaranteed".
120 */
121 public String getStatisticsAccuracyDescription() {
122 updateIfNeeded();
123 return statistics.getStatisticsAccuracyDescription();
124 }
125
126 /**
127 * @return the name of the Ehcache, or null is there no associated cache
128 */
129 public String getAssociatedCacheName() {
130 if (statistics == null) {
131 return null;
132 } else {
133 return statistics.getAssociatedCacheName();
134 }
135 }
136
137 /**
138 * Clears the statistic counters to 0 for the associated Cache.
139 */
140 public void clearStatistics() {
141 statistics.clearStatistics();
142 }
143
144 /**
145 * The number of times a requested item was found in the cache.
146 * <p/>
147 * Warning. This statistic is recorded as a long. If the statistic is large than Integer#MAX_VALUE
148 * precision will be lost.
149 *
150 * @return the number of times a requested item was found in the cache
151 */
152 public long getCacheHits() {
153 updateIfNeeded();
154 return statistics.getCacheHits();
155 }
156
157 /**
158 * Number of times a requested item was found in the Memory Store.
159 *
160 * @return the number of times a requested item was found in memory
161 */
162 public long getInMemoryHits() {
163 updateIfNeeded();
164 return statistics.getInMemoryHits();
165 }
166
167 /**
168 * Number of times a requested item was found in the off-heap store.
169 *
170 * @return the number of times a requested item was found in off-heap
171 */
172 public long getOffHeapHits() {
173 updateIfNeeded();
174 return statistics.getOffHeapHits();
175 }
176
177 /**
178 * Number of times a requested item was found in the Disk Store.
179 *
180 * @return the number of times a requested item was found on Disk, or 0 if there is no disk storage configured.
181 */
182 public long getOnDiskHits() {
183 updateIfNeeded();
184 return statistics.getOnDiskHits();
185 }
186
187 /**
188 * Warning. This statistic is recorded as a long. If the statistic is large than Integer#MAX_VALUE
189 * precision will be lost.
190 *
191 * @return the number of times a requested element was not found in the cache
192 */
193 public long getCacheMisses() {
194 updateIfNeeded();
195 return statistics.getCacheMisses();
196 }
197
198 /** {@inheritDoc} */
199 public long getInMemoryMisses() {
200 updateIfNeeded();
201 return statistics.getInMemoryMisses();
202 }
203
204 /** {@inheritDoc} */
205 public long getOffHeapMisses() {
206 updateIfNeeded();
207 return statistics.getOffHeapMisses();
208 }
209
210 /** {@inheritDoc} */
211 public long getOnDiskMisses() {
212 updateIfNeeded();
213 return statistics.getOnDiskMisses();
214 }
215
216 /**
217 * Gets the number of elements stored in the cache. Caclulating this can be expensive. Accordingly,
218 * this method will return three different values, depending on the statistics accuracy setting.
219 * <h3>Best Effort Size</h3>
220 * This result is returned when the statistics accuracy setting is {@link net.sf.ehcache.Statistics#STATISTICS_ACCURACY_BEST_EFFORT}.
221 * <p/>
222 * The size is the number of {@link net.sf.ehcache.Element}s in the {@link net.sf.ehcache.store.MemoryStore} plus
223 * the number of {@link net.sf.ehcache.Element}s in the {@link net.sf.ehcache.store.disk.DiskStore}.
224 * <p/>
225 * This number is the actual number of elements, including expired elements that have
226 * not been removed. Any duplicates between stores are accounted for.
227 * <p/>
228 * Expired elements are removed from the the memory store when
229 * getting an expired element, or when attempting to spool an expired element to
230 * disk.
231 * <p/>
232 * Expired elements are removed from the disk store when getting an expired element,
233 * or when the expiry thread runs, which is once every five minutes.
234 * <p/>
235 * <h3>Guaranteed Accuracy Size</h3>
236 * This result is returned when the statistics accuracy setting is {@link net.sf.ehcache.Statistics#STATISTICS_ACCURACY_GUARANTEED}.
237 * <p/>
238 * This method accounts for elements which might be expired or duplicated between stores. It take approximately
239 * 200ms per 1000 elements to execute.
240 * <h3>Fast but non-accurate Size</h3>
241 * This result is returned when the statistics accuracy setting is {@link Statistics#STATISTICS_ACCURACY_NONE}.
242 * <p/>
243 * The number given may contain expired elements. In addition if the DiskStore is used it may contain some double
244 * counting of elements. It takes 6ms for 1000 elements to execute. Time to execute is O(log n). 50,000 elements take
245 * 36ms.
246 *
247 * @return the number of elements in the ehcache, with a varying degree of accuracy, depending on accuracy setting.
248 */
249 public long getObjectCount() {
250 updateIfNeeded();
251 return statistics.getObjectCount();
252 }
253
254 /**
255 * Gets the size of the write-behind queue, if any.
256 * The value is for all local buckets
257 * @return Elements waiting to be processed by the write behind writer. -1 if no write-behind
258 */
259 public long getWriterQueueLength() {
260 updateIfNeeded();
261 return statistics.getWriterQueueSize();
262 }
263
264 /**
265 * {@inheritDoc}
266 */
267 public int getWriterMaxQueueSize() {
268 return ehcache.getCacheConfiguration().getCacheWriterConfiguration().getWriteBehindMaxQueueSize();
269 }
270
271 /**
272 * Gets the number of objects in the MemoryStore
273 * @return the MemoryStore size which is always a count unadjusted for duplicates or expiries
274 */
275 public long getMemoryStoreObjectCount() {
276 updateIfNeeded();
277 return statistics.getMemoryStoreObjectCount();
278 }
279
280 /**
281 * {@inheritDoc}
282 */
283 public long getOffHeapStoreObjectCount() {
284 updateIfNeeded();
285 return statistics.getOffHeapStoreObjectCount();
286 }
287
288 /**
289 * Gets the number of objects in the DiskStore
290 * @return the DiskStore size which is always a count unadjusted for duplicates or expiries
291 */
292 public long getDiskStoreObjectCount() {
293 updateIfNeeded();
294 return statistics.getDiskStoreObjectCount();
295 }
296
297
298 /**
299 * @return the object name for this MBean
300 */
301 ObjectName getObjectName() {
302 return objectName;
303 }
304
305 /**
306 * Return the backing cache.
307 * @return the backing cache, if one is connected. On Serialization
308 * the transient Ehcache reference is dropped.
309 */
310 public Ehcache getEhcache() {
311 return ehcache;
312 }
313
314 private static double getPercentage(long number, long total) {
315 if (total == 0) {
316 return 0.0;
317 } else {
318 return number / (double)total;
319 }
320 }
321
322 /**
323 * {@inheritDoc}
324 */
325 public double getCacheHitPercentage() {
326 updateIfNeeded();
327 long hits = statistics.getCacheHits();
328 long misses = statistics.getCacheMisses();
329
330 long total = hits + misses;
331 return getPercentage(hits, total);
332 }
333
334 /**
335 * {@inheritDoc}
336 */
337 public double getCacheMissPercentage() {
338 updateIfNeeded();
339 long hits = statistics.getCacheHits();
340 long misses = statistics.getCacheMisses();
341
342 long total = hits + misses;
343 return getPercentage(misses, total);
344 }
345
346 /**
347 * {@inheritDoc}
348 */
349 public double getInMemoryHitPercentage() {
350 updateIfNeeded();
351 long hits = statistics.getInMemoryHits();
352 long misses = statistics.getInMemoryMisses();
353
354 long total = hits + misses;
355 return getPercentage(hits, total);
356 }
357
358 /**
359 * {@inheritDoc}
360 */
361 public double getOffHeapHitPercentage() {
362 updateIfNeeded();
363 long hits = statistics.getOffHeapHits();
364 long misses = statistics.getOffHeapMisses();
365
366 long total = hits + misses;
367 return getPercentage(hits, total);
368 }
369
370 /**
371 * {@inheritDoc}
372 */
373 public double getOnDiskHitPercentage() {
374 updateIfNeeded();
375 long hits = statistics.getOnDiskHits();
376 long misses = statistics.getOnDiskMisses();
377
378 long total = hits + misses;
379 return getPercentage(hits, total);
380 }
381 }
382