• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ben-manes / caffeine / #5173

29 Dec 2025 05:27AM UTC coverage: 0.0% (-100.0%) from 100.0%
#5173

push

github

ben-manes
speed up development ci build

0 of 3838 branches covered (0.0%)

0 of 7869 relevant lines covered (0.0%)

0.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/caffeine/src/main/java/com/github/benmanes/caffeine/cache/stats/CacheStats.java
1
/*
2
 * Copyright 2014 Ben Manes. All Rights Reserved.
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
package com.github.benmanes.caffeine.cache.stats;
17

18
import java.util.Objects;
19

20
import org.jspecify.annotations.NullMarked;
21
import org.jspecify.annotations.Nullable;
22

23
import com.github.benmanes.caffeine.cache.Cache;
24
import com.github.benmanes.caffeine.cache.LoadingCache;
25
import com.google.errorprone.annotations.Immutable;
26

27
/**
28
 * Statistics about the performance of a {@link Cache}.
29
 * <p>
30
 * Cache statistics are incremented according to the following rules:
31
 * <ul>
32
 *   <li>When a cache lookup encounters an existing cache entry {@code hitCount} is incremented.
33
 *   <li>When a cache lookup first encounters a missing cache entry, a new entry is loaded.
34
 *   <ul>
35
 *     <li>After successfully loading an entry {@code missCount} and {@code loadSuccessCount} are
36
 *         incremented, and the total loading time, in nanoseconds, is added to
37
 *         {@code totalLoadTime}.
38
 *     <li>When an exception is thrown while loading an entry or if the loaded value is {code null},
39
 *         {@code missCount} and {@code loadFailureCount} are incremented, and the total loading
40
 *         time, in nanoseconds, is added to {@code totalLoadTime}.
41
 *     <li>Cache lookups that encounter a missing cache entry that is still loading will wait
42
 *         for loading to complete (whether successful or not) and then increment {@code missCount}.
43
 *   </ul>
44
 *   <li>When an entry is computed through the {@linkplain Cache#asMap asMap} the
45
 *       {@code loadSuccessCount} or {@code loadFailureCount} is incremented.
46
 *   <li>When an entry is evicted from the cache, {@code evictionCount} is incremented and the
47
 *       weight added to {@code evictionWeight}.
48
 *   <li>No stats are modified when a cache entry is invalidated or manually removed.
49
 *   <li>No stats are modified by non-computing operations invoked on the
50
 *       {@linkplain Cache#asMap asMap} view of the cache.
51
 * </ul>
52
 * <p>
53
 * A lookup is specifically defined as an invocation of one of the methods
54
 * {@link LoadingCache#get(Object)}, {@link Cache#get(Object, java.util.function.Function)}, or
55
 * {@link LoadingCache#getAll(Iterable)}.
56
 * <p>
57
 * This is a <em>value-based</em> class; use of identity-sensitive operations (including reference
58
 * equality ({@code ==}), identity hash code, or synchronization) on instances of {@code CacheStats}
59
 * may have unpredictable results and should be avoided.
60
 *
61
 * @author ben.manes@gmail.com (Ben Manes)
62
 */
63
@Immutable
64
@NullMarked
65
public final class CacheStats {
66
  private static final CacheStats EMPTY_STATS = CacheStats.of(0L, 0L, 0L, 0L, 0L, 0L, 0L);
×
67

68
  private final long hitCount;
69
  private final long missCount;
70
  private final long loadSuccessCount;
71
  private final long loadFailureCount;
72
  private final long totalLoadTime;
73
  private final long evictionCount;
74
  private final long evictionWeight;
75

76
  private CacheStats(long hitCount, long missCount, long loadSuccessCount, long loadFailureCount,
77
      long totalLoadTime, long evictionCount, long evictionWeight) {
×
78
    if ((hitCount < 0) || (missCount < 0) || (loadSuccessCount < 0) || (loadFailureCount < 0)
×
79
        || (totalLoadTime < 0) || (evictionCount < 0) || (evictionWeight < 0)) {
80
      throw new IllegalArgumentException();
×
81
    }
82
    this.hitCount = hitCount;
×
83
    this.missCount = missCount;
×
84
    this.loadSuccessCount = loadSuccessCount;
×
85
    this.loadFailureCount = loadFailureCount;
×
86
    this.totalLoadTime = totalLoadTime;
×
87
    this.evictionCount = evictionCount;
×
88
    this.evictionWeight = evictionWeight;
×
89
  }
×
90

91
  /**
92
   * Returns a {@code CacheStats} representing the specified statistics.
93
   *
94
   * @param hitCount the number of cache hits
95
   * @param missCount the number of cache misses
96
   * @param loadSuccessCount the number of successful cache loads
97
   * @param loadFailureCount the number of failed cache loads
98
   * @param totalLoadTime the total load time (success and failure)
99
   * @param evictionCount the number of entries evicted from the cache
100
   * @param evictionWeight the sum of weights of entries evicted from the cache
101
   * @return a {@code CacheStats} representing the specified statistics
102
   * @throws IllegalArgumentException if the metric is negative
103
   */
104
  public static CacheStats of(long hitCount, long missCount, long loadSuccessCount,
105
      long loadFailureCount, long totalLoadTime, long evictionCount, long evictionWeight) {
106
    // Many parameters of the same type in a row is a bad thing, but this class is not constructed
107
    // by end users and is too fine-grained for a builder.
108
    return new CacheStats(hitCount, missCount, loadSuccessCount,
×
109
        loadFailureCount, totalLoadTime, evictionCount, evictionWeight);
110
  }
111

112
  /**
113
   * Returns a statistics instance where no cache events have been recorded.
114
   *
115
   * @return an empty statistics instance
116
   */
117
  public static CacheStats empty() {
118
    return EMPTY_STATS;
×
119
  }
120

121
  /**
122
   * Returns the number of times {@link Cache} lookup methods have returned either a cached or
123
   * uncached value. This is defined as {@code hitCount + missCount}.
124
   * <p>
125
   * <b>Note:</b> the values of the metrics are undefined in case of overflow (though it is
126
   * guaranteed not to throw an exception). If you require specific handling, we recommend
127
   * implementing your own stats collector.
128
   *
129
   * @return the {@code hitCount + missCount}
130
   */
131
  public long requestCount() {
132
    return saturatedAdd(hitCount, missCount);
×
133
  }
134

135
  /**
136
   * Returns the number of times {@link Cache} lookup methods have returned a cached value.
137
   *
138
   * @return the number of times {@link Cache} lookup methods have returned a cached value
139
   */
140
  public long hitCount() {
141
    return hitCount;
×
142
  }
143

144
  /**
145
   * Returns the ratio of cache requests which were hits. This is defined as
146
   * {@code hitCount / requestCount}, or {@code 1.0} when {@code requestCount == 0}. Note that
147
   * {@code hitRate + missRate =~ 1.0}.
148
   *
149
   * @return the ratio of cache requests which were hits
150
   */
151
  public double hitRate() {
152
    long requestCount = requestCount();
×
153
    return (requestCount == 0) ? 1.0 : (double) hitCount / requestCount;
×
154
  }
155

156
  /**
157
   * Returns the number of times {@link Cache} lookup methods have returned an uncached (newly
158
   * loaded) value, or null. Multiple concurrent calls to {@link Cache} lookup methods on an absent
159
   * value can result in multiple misses, all returning the results of a single cache load
160
   * operation.
161
   *
162
   * @return the number of times {@link Cache} lookup methods have returned an uncached (newly
163
   *         loaded) value, or null
164
   */
165
  public long missCount() {
166
    return missCount;
×
167
  }
168

169
  /**
170
   * Returns the ratio of cache requests which were misses. This is defined as
171
   * {@code missCount / requestCount}, or {@code 0.0} when {@code requestCount == 0}.
172
   * Note that {@code hitRate + missRate =~ 1.0}. Cache misses include all requests which
173
   * weren't cache hits, including requests which resulted in either successful or failed loading
174
   * attempts, and requests which waited for other threads to finish loading. It is thus the case
175
   * that {@code missCount >= loadSuccessCount + loadFailureCount}. Multiple
176
   * concurrent misses for the same key will result in a single load operation.
177
   *
178
   * @return the ratio of cache requests which were misses
179
   */
180
  public double missRate() {
181
    long requestCount = requestCount();
×
182
    return (requestCount == 0) ? 0.0 : (double) missCount / requestCount;
×
183
  }
184

185
  /**
186
   * Returns the total number of times that {@link Cache} lookup methods attempted to load new
187
   * values. This includes both successful load operations and those that threw exceptions.
188
   * This is defined as {@code loadSuccessCount + loadFailureCount}.
189
   * <p>
190
   * <b>Note:</b> the values of the metrics are undefined in case of overflow (though it is
191
   * guaranteed not to throw an exception). If you require specific handling, we recommend
192
   * implementing your own stats collector.
193
   *
194
   * @return the {@code loadSuccessCount + loadFailureCount}
195
   */
196
  public long loadCount() {
197
    return saturatedAdd(loadSuccessCount, loadFailureCount);
×
198
  }
199

200
  /**
201
   * Returns the number of times {@link Cache} lookup methods have successfully loaded a new value.
202
   * This is always incremented in conjunction with {@link #missCount}, though {@code missCount}
203
   * is also incremented when an exception is encountered during cache loading (see
204
   * {@link #loadFailureCount}). Multiple concurrent misses for the same key will result in a
205
   * single load operation.
206
   *
207
   * @return the number of times {@link Cache} lookup methods have successfully loaded a new value
208
   */
209
  public long loadSuccessCount() {
210
    return loadSuccessCount;
×
211
  }
212

213
  /**
214
   * Returns the number of times {@link Cache} lookup methods failed to load a new value, either
215
   * because no value was found or an exception was thrown while loading. This is always incremented
216
   * in conjunction with {@code missCount}, though {@code missCount} is also incremented when cache
217
   * loading completes successfully (see {@link #loadSuccessCount}). Multiple concurrent misses for
218
   * the same key will result in a single load operation.
219
   *
220
   * @return the number of times {@link Cache} lookup methods failed to load a new value
221
   */
222
  public long loadFailureCount() {
223
    return loadFailureCount;
×
224
  }
225

226
  /**
227
   * Returns the ratio of cache loading attempts which threw exceptions. This is defined as
228
   * {@code loadFailureCount / (loadSuccessCount + loadFailureCount)}, or {@code 0.0} when
229
   * {@code loadSuccessCount + loadFailureCount == 0}.
230
   * <p>
231
   * <b>Note:</b> the values of the metrics are undefined in case of overflow (though it is
232
   * guaranteed not to throw an exception). If you require specific handling, we recommend
233
   * implementing your own stats collector.
234
   *
235
   * @return the ratio of cache loading attempts which threw exceptions
236
   */
237
  public double loadFailureRate() {
238
    long totalLoadCount = saturatedAdd(loadSuccessCount, loadFailureCount);
×
239
    return (totalLoadCount == 0) ? 0.0 : (double) loadFailureCount / totalLoadCount;
×
240
  }
241

242
  /**
243
   * Returns the total number of nanoseconds the cache has spent loading new values. This can be
244
   * used to calculate the miss penalty. This value is increased every time {@code loadSuccessCount}
245
   * or {@code loadFailureCount} is incremented.
246
   *
247
   * @return the total number of nanoseconds the cache has spent loading new values
248
   */
249
  public long totalLoadTime() {
250
    return totalLoadTime;
×
251
  }
252

253
  /**
254
   * Returns the average number of nanoseconds spent loading new values. This is defined as
255
   * {@code totalLoadTime / (loadSuccessCount + loadFailureCount)}.
256
   * <p>
257
   * <b>Note:</b> the values of the metrics are undefined in case of overflow (though it is
258
   * guaranteed not to throw an exception). If you require specific handling, we recommend
259
   * implementing your own stats collector.
260
   *
261
   * @return the average number of nanoseconds spent loading new values
262
   */
263
  public double averageLoadPenalty() {
264
    long totalLoadCount = saturatedAdd(loadSuccessCount, loadFailureCount);
×
265
    return (totalLoadCount == 0) ? 0.0 : (double) totalLoadTime / totalLoadCount;
×
266
  }
267

268
  /**
269
   * Returns the number of times an entry has been evicted. This count does not include manual
270
   * {@linkplain Cache#invalidate invalidations}.
271
   *
272
   * @return the number of times an entry has been evicted
273
   */
274
  public long evictionCount() {
275
    return evictionCount;
×
276
  }
277

278
  /**
279
   * Returns the sum of weights of evicted entries. This total does not include manual
280
   * {@linkplain Cache#invalidate invalidations}.
281
   *
282
   * @return the sum of weights of evicted entities
283
   */
284
  public long evictionWeight() {
285
    return evictionWeight;
×
286
  }
287

288
  /**
289
   * Returns a new {@code CacheStats} representing the difference between this {@code CacheStats}
290
   * and {@code other}. Negative values, which aren't supported by {@code CacheStats} will be
291
   * rounded up to zero.
292
   *
293
   * @param other the statistics to subtract with
294
   * @return the difference between this instance and {@code other}
295
   */
296
  public CacheStats minus(CacheStats other) {
297
    return CacheStats.of(
×
298
        Math.max(0L, hitCount - other.hitCount),
×
299
        Math.max(0L, missCount - other.missCount),
×
300
        Math.max(0L, loadSuccessCount - other.loadSuccessCount),
×
301
        Math.max(0L, loadFailureCount - other.loadFailureCount),
×
302
        Math.max(0L, totalLoadTime - other.totalLoadTime),
×
303
        Math.max(0L, evictionCount - other.evictionCount),
×
304
        Math.max(0L, evictionWeight - other.evictionWeight));
×
305
  }
306

307
  /**
308
   * Returns a new {@code CacheStats} representing the sum of this {@code CacheStats} and
309
   * {@code other}.
310
   * <p>
311
   * <b>Note:</b> the values of the metrics are undefined in case of overflow (though it is
312
   * guaranteed not to throw an exception). If you require specific handling, we recommend
313
   * implementing your own stats collector.
314
   *
315
   * @param other the statistics to add with
316
   * @return the sum of the statistics
317
   */
318
  public CacheStats plus(CacheStats other) {
319
    return CacheStats.of(
×
320
        saturatedAdd(hitCount, other.hitCount),
×
321
        saturatedAdd(missCount, other.missCount),
×
322
        saturatedAdd(loadSuccessCount, other.loadSuccessCount),
×
323
        saturatedAdd(loadFailureCount, other.loadFailureCount),
×
324
        saturatedAdd(totalLoadTime, other.totalLoadTime),
×
325
        saturatedAdd(evictionCount, other.evictionCount),
×
326
        saturatedAdd(evictionWeight, other.evictionWeight));
×
327
  }
328

329
  /**
330
   * Returns the sum of {@code a} and {@code b} unless it would overflow or underflow in which case
331
   * {@code Long.MAX_VALUE} or {@code Long.MIN_VALUE} is returned, respectively.
332
   */
333
  @SuppressWarnings("ShortCircuitBoolean")
334
  static long saturatedAdd(long a, long b) {
335
    long naiveSum = a + b;
×
336
    if (((a ^ b) < 0) | ((a ^ naiveSum) >= 0)) {
×
337
      // If a and b have different signs or a has the same sign as the result then there was no
338
      // overflow, return.
339
      return naiveSum;
×
340
    }
341
    // we did over/under flow, if the sign is negative we should return MAX otherwise MIN
342
    return Long.MAX_VALUE + ((naiveSum >>> (Long.SIZE - 1)) ^ 1);
×
343
  }
344

345
  @Override
346
  public int hashCode() {
347
    return Objects.hash(hitCount, missCount, loadSuccessCount,
×
348
        loadFailureCount, totalLoadTime, evictionCount, evictionWeight);
×
349
  }
350

351
  @Override
352
  public boolean equals(@Nullable Object o) {
353
    if (o == this) {
×
354
      return true;
×
355
    } else if (!(o instanceof CacheStats)) {
×
356
      return false;
×
357
    }
358
    var other = (CacheStats) o;
×
359
    return hitCount == other.hitCount
×
360
        && missCount == other.missCount
361
        && loadSuccessCount == other.loadSuccessCount
362
        && loadFailureCount == other.loadFailureCount
363
        && totalLoadTime == other.totalLoadTime
364
        && evictionCount == other.evictionCount
365
        && evictionWeight == other.evictionWeight;
366
  }
367

368
  @Override
369
  public String toString() {
370
    return getClass().getSimpleName() + '{'
×
371
        + "hitCount=" + hitCount + ", "
372
        + "missCount=" + missCount + ", "
373
        + "loadSuccessCount=" + loadSuccessCount + ", "
374
        + "loadFailureCount=" + loadFailureCount + ", "
375
        + "totalLoadTime=" + totalLoadTime + ", "
376
        + "evictionCount=" + evictionCount + ", "
377
        + "evictionWeight=" + evictionWeight
378
        + '}';
379
  }
380
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc