Skip to content

Commit 6daeb1a

Browse files
mp911dechristophstrobl
authored andcommitted
DATAREDIS-1082 - Expose cache metrics for RedisCache.
We now expose CacheStatistics for RedisCache. Each cache tracks its statistics (retrievals, hits, misses, stores, removals, lock wait) locally. CacheStatistics is exposed through RedisCacheWriter to allow for statistics customization depending on the cache writer strategy. The statistic object is a singleton per cache instance that gets updated with as the cache operates. RedisCache cache = …; CacheStatistics statistics = cache.getStatistics(); statistics.getRetrievals(); statistics.getHits(); Original Pull Request: spring-projects#545
1 parent 488e658 commit 6daeb1a

File tree

8 files changed

+363
-23
lines changed

8 files changed

+363
-23
lines changed

src/main/asciidoc/new-features.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33

44
This section briefly covers items that are new and noteworthy in the latest releases.
55

6+
[[new-in-2.4.0]]
7+
== New in Spring Data Redis 2.4
8+
9+
* `RedisCache` now exposes `CacheStatistics`.
10+
611
[[new-in-2.3.0]]
12+
713
== New in Spring Data Redis 2.3
814

915
* Template API Method Refinements for `Duration` and `Instant`.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
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+
* https://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 org.springframework.data.redis.cache;
17+
18+
import java.util.concurrent.TimeUnit;
19+
20+
/**
21+
* Cache statistics for a Redis Cache.
22+
*
23+
* @author Mark Paluch
24+
* @since 2.4
25+
*/
26+
public interface CacheStatistics {
27+
28+
/**
29+
* @return number of put operations on the cache.
30+
*/
31+
long getStores();
32+
33+
/**
34+
* @return number of get operations.
35+
*/
36+
long getRetrievals();
37+
38+
/**
39+
* @return number of cache get hits.
40+
*/
41+
long getHits();
42+
43+
/**
44+
* @return number of cache get misses.
45+
*/
46+
long getMisses();
47+
48+
/**
49+
* @return number of cache removals.
50+
*/
51+
long getRemovals();
52+
53+
/**
54+
* @param unit the time unit to report the lock wait duration.
55+
* @return lock duration using the given {@link TimeUnit} if the cache is configured to use locking.
56+
*/
57+
long getLockWaitDuration(TimeUnit unit);
58+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
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+
* https://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 org.springframework.data.redis.cache;
17+
18+
import java.util.concurrent.TimeUnit;
19+
import java.util.concurrent.atomic.LongAdder;
20+
21+
import org.springframework.util.Assert;
22+
23+
/**
24+
* Default mutable {@link CacheStatistics} implementation.
25+
*
26+
* @author Mark Paluch
27+
* @since 2.4
28+
*/
29+
class DefaultCacheStatistics implements CacheStatistics {
30+
31+
private final LongAdder stores = new LongAdder();
32+
private final LongAdder retrievals = new LongAdder();
33+
private final LongAdder hits = new LongAdder();
34+
private final LongAdder misses = new LongAdder();
35+
private final LongAdder removals = new LongAdder();
36+
private final LongAdder lockWaitTimeNs = new LongAdder();
37+
38+
/*
39+
* (non-Javadoc)
40+
* @see org.springframework.data.redis.cache.CacheStatistics#getStores()
41+
*/
42+
@Override
43+
public long getStores() {
44+
return stores.sum();
45+
}
46+
47+
void incrementStores() {
48+
stores.increment();
49+
}
50+
51+
/*
52+
* (non-Javadoc)
53+
* @see org.springframework.data.redis.cache.CacheStatistics#getRetrievals()
54+
*/
55+
@Override
56+
public long getRetrievals() {
57+
return retrievals.sum();
58+
}
59+
60+
void incrementRetrievals() {
61+
retrievals.increment();
62+
}
63+
64+
/*
65+
* (non-Javadoc)
66+
* @see org.springframework.data.redis.cache.CacheStatistics#getHits()
67+
*/
68+
@Override
69+
public long getHits() {
70+
return hits.sum();
71+
}
72+
73+
void incrementHits() {
74+
hits.increment();
75+
}
76+
77+
/*
78+
* (non-Javadoc)
79+
* @see org.springframework.data.redis.cache.CacheStatistics#getMisses()
80+
*/
81+
@Override
82+
public long getMisses() {
83+
return misses.sum();
84+
}
85+
86+
void incrementMisses() {
87+
misses.increment();
88+
}
89+
90+
/*
91+
* (non-Javadoc)
92+
* @see org.springframework.data.redis.cache.CacheStatistics#getRemovals()
93+
*/
94+
@Override
95+
public long getRemovals() {
96+
return removals.sum();
97+
}
98+
99+
void incrementRemovals() {
100+
incrementRemovals(1);
101+
}
102+
103+
/**
104+
* @param x number of removals to add.
105+
*/
106+
void incrementRemovals(int x) {
107+
removals.add(x);
108+
}
109+
110+
/*
111+
* (non-Javadoc)
112+
* @see org.springframework.data.redis.cache.CacheStatistics#getLockWaitDuration(java.util.concurrent.TimeUnit)
113+
*/
114+
@Override
115+
public long getLockWaitDuration(TimeUnit unit) {
116+
117+
Assert.notNull(unit, "TimeUnit must not be null");
118+
119+
return unit.convert(lockWaitTimeNs.sum(), TimeUnit.NANOSECONDS);
120+
}
121+
122+
void incrementLockWaitTime(long waitTimeNs) {
123+
lockWaitTimeNs.add(waitTimeNs);
124+
}
125+
}

src/main/java/org/springframework/data/redis/cache/DefaultRedisCacheWriter.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class DefaultRedisCacheWriter implements RedisCacheWriter {
5151

5252
private final RedisConnectionFactory connectionFactory;
5353
private final Duration sleepTime;
54+
private final DefaultCacheStatistics statistics;
5455

5556
/**
5657
* @param connectionFactory must not be {@literal null}.
@@ -71,6 +72,7 @@ class DefaultRedisCacheWriter implements RedisCacheWriter {
7172

7273
this.connectionFactory = connectionFactory;
7374
this.sleepTime = sleepTime;
75+
this.statistics = new DefaultCacheStatistics();
7476
}
7577

7678
/*
@@ -94,6 +96,8 @@ public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
9496

9597
return "OK";
9698
});
99+
100+
statistics.incrementStores();
97101
}
98102

99103
/*
@@ -106,7 +110,17 @@ public byte[] get(String name, byte[] key) {
106110
Assert.notNull(name, "Name must not be null!");
107111
Assert.notNull(key, "Key must not be null!");
108112

109-
return execute(name, connection -> connection.get(key));
113+
byte[] result = execute(name, connection -> connection.get(key));
114+
115+
statistics.incrementRetrievals();
116+
117+
if (result != null) {
118+
statistics.incrementHits();
119+
} else {
120+
statistics.incrementMisses();
121+
}
122+
123+
return result;
110124
}
111125

112126
/*
@@ -132,6 +146,8 @@ public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Durat
132146
if (shouldExpireWithin(ttl)) {
133147
connection.pExpire(key, ttl.toMillis());
134148
}
149+
150+
statistics.incrementStores();
135151
return null;
136152
}
137153

@@ -156,6 +172,7 @@ public void remove(String name, byte[] key) {
156172
Assert.notNull(key, "Key must not be null!");
157173

158174
execute(name, connection -> connection.del(key));
175+
statistics.incrementRemovals();
159176
}
160177

161178
/*
@@ -183,6 +200,7 @@ public void clean(String name, byte[] pattern) {
183200
.toArray(new byte[0][]);
184201

185202
if (keys.length > 0) {
203+
statistics.incrementRemovals(keys.length);
186204
connection.del(keys);
187205
}
188206
} finally {
@@ -196,6 +214,15 @@ public void clean(String name, byte[] pattern) {
196214
});
197215
}
198216

217+
/*
218+
* (non-Javadoc)
219+
* @see org.springframework.data.redis.cache.RedisCacheWriter#getStatistics()
220+
*/
221+
@Override
222+
public DefaultCacheStatistics getStatistics() {
223+
return statistics;
224+
}
225+
199226
/**
200227
* Explicitly set a write lock on a cache.
201228
*
@@ -262,6 +289,7 @@ private void checkAndPotentiallyWaitUntilUnlocked(String name, RedisConnection c
262289
return;
263290
}
264291

292+
long lockWaitTimeNs = System.nanoTime();
265293
try {
266294

267295
while (doCheckLock(name, connection)) {
@@ -274,6 +302,8 @@ private void checkAndPotentiallyWaitUntilUnlocked(String name, RedisConnection c
274302

275303
throw new PessimisticLockingFailureException(String.format("Interrupted while waiting to unlock cache %s", name),
276304
ex);
305+
} finally {
306+
statistics.incrementLockWaitTime(System.nanoTime() - lockWaitTimeNs);
277307
}
278308
}
279309

src/main/java/org/springframework/data/redis/cache/RedisCache.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,17 @@ public void clear() {
193193
cacheWriter.clean(name, pattern);
194194
}
195195

196+
/**
197+
* Return the {@link CacheStatistics} for this cache instance. Statistics are accumulated per cache instance and not
198+
* from the backing Redis data store. Cache statistics are accumulated starting from the time a cache is created.
199+
*
200+
* @return statistics object for this {@link RedisCache}.
201+
* @since 2.4
202+
*/
203+
public CacheStatistics getStatistics() {
204+
return cacheWriter.getStatistics();
205+
}
206+
196207
/**
197208
* Get {@link RedisCacheConfiguration} used.
198209
*

src/main/java/org/springframework/data/redis/cache/RedisCacheWriter.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,13 @@ static RedisCacheWriter lockingRedisCacheWriter(RedisConnectionFactory connectio
106106
* @param pattern The pattern for the keys to remove. Must not be {@literal null}.
107107
*/
108108
void clean(String name, byte[] pattern);
109+
110+
/**
111+
* Return the {@link CacheStatistics} for this cache instance. Statistics are accumulated per cache instance and not
112+
* from the backing Redis data store. Cache statistics are accumulated starting from the time a cache is created.
113+
*
114+
* @return statistics object for this {@link RedisCache}.
115+
* @since 2.4
116+
*/
117+
CacheStatistics getStatistics();
109118
}

0 commit comments

Comments
 (0)