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: #545
This commit is contained in:
committed by
Christoph Strobl
parent
488e658f5d
commit
6daeb1a04c
80
src/test/java/org/springframework/data/redis/cache/DefaultCacheStatisticsUnitTests.java
vendored
Normal file
80
src/test/java/org/springframework/data/redis/cache/DefaultCacheStatisticsUnitTests.java
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.cache;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DefaultCacheStatistics}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class DefaultCacheStatisticsUnitTests {
|
||||
|
||||
DefaultCacheStatistics statistics = new DefaultCacheStatistics();
|
||||
|
||||
@Test // DATAREDIS-1082
|
||||
void shouldReportRetrievals() {
|
||||
|
||||
assertThat(statistics.getRetrievals()).isZero();
|
||||
|
||||
statistics.incrementRetrievals();
|
||||
|
||||
assertThat(statistics.getRetrievals()).isOne();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-1082
|
||||
void shouldReportHits() {
|
||||
|
||||
assertThat(statistics.getHits()).isZero();
|
||||
|
||||
statistics.incrementHits();
|
||||
|
||||
assertThat(statistics.getHits()).isOne();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-1082
|
||||
void shouldReportMisses() {
|
||||
|
||||
assertThat(statistics.getMisses()).isZero();
|
||||
|
||||
statistics.incrementMisses();
|
||||
|
||||
assertThat(statistics.getMisses()).isOne();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-1082
|
||||
void shouldReportPuts() {
|
||||
|
||||
assertThat(statistics.getStores()).isZero();
|
||||
|
||||
statistics.incrementStores();
|
||||
|
||||
assertThat(statistics.getStores()).isOne();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-1082
|
||||
void shouldReportRemovals() {
|
||||
|
||||
assertThat(statistics.getRemovals()).isZero();
|
||||
|
||||
statistics.incrementRemovals();
|
||||
|
||||
assertThat(statistics.getRemovals()).isOne();
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,8 @@ import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.types.Expiration;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link DefaultRedisCacheWriter}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@@ -85,15 +87,19 @@ public class DefaultRedisCacheWriterTests {
|
||||
doWithConnection(RedisConnection::flushAll);
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-481
|
||||
@Test // DATAREDIS-481, DATAREDIS-1082
|
||||
public void putShouldAddEternalEntry() {
|
||||
|
||||
nonLockingRedisCacheWriter(connectionFactory).put(CACHE_NAME, binaryCacheKey, binaryCacheValue, Duration.ZERO);
|
||||
RedisCacheWriter writer = nonLockingRedisCacheWriter(connectionFactory);
|
||||
writer.put(CACHE_NAME, binaryCacheKey, binaryCacheValue, Duration.ZERO);
|
||||
|
||||
doWithConnection(connection -> {
|
||||
assertThat(connection.get(binaryCacheKey)).isEqualTo(binaryCacheValue);
|
||||
assertThat(connection.ttl(binaryCacheKey)).isEqualTo(-1);
|
||||
});
|
||||
|
||||
assertThat(writer.getStatistics().getStores()).isOne();
|
||||
assertThat(writer.getStatistics().getLockWaitDuration(TimeUnit.NANOSECONDS)).isZero();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-481
|
||||
@@ -136,13 +142,18 @@ public class DefaultRedisCacheWriterTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-481
|
||||
@Test // DATAREDIS-481, DATAREDIS-1082
|
||||
public void getShouldReturnValue() {
|
||||
|
||||
doWithConnection(connection -> connection.set(binaryCacheKey, binaryCacheValue));
|
||||
|
||||
assertThat(nonLockingRedisCacheWriter(connectionFactory).get(CACHE_NAME, binaryCacheKey))
|
||||
RedisCacheWriter writer = nonLockingRedisCacheWriter(connectionFactory);
|
||||
assertThat(writer.get(CACHE_NAME, binaryCacheKey))
|
||||
.isEqualTo(binaryCacheValue);
|
||||
|
||||
assertThat(writer.getStatistics().getRetrievals()).isOne();
|
||||
assertThat(writer.getStatistics().getHits()).isOne();
|
||||
assertThat(writer.getStatistics().getMisses()).isZero();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-481
|
||||
@@ -150,52 +161,61 @@ public class DefaultRedisCacheWriterTests {
|
||||
assertThat(nonLockingRedisCacheWriter(connectionFactory).get(CACHE_NAME, binaryCacheKey)).isNull();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-481
|
||||
@Test // DATAREDIS-481, DATAREDIS-1082
|
||||
public void putIfAbsentShouldAddEternalEntryWhenKeyDoesNotExist() {
|
||||
|
||||
assertThat(nonLockingRedisCacheWriter(connectionFactory).putIfAbsent(CACHE_NAME, binaryCacheKey, binaryCacheValue,
|
||||
RedisCacheWriter writer = nonLockingRedisCacheWriter(connectionFactory);
|
||||
assertThat(writer.putIfAbsent(CACHE_NAME, binaryCacheKey, binaryCacheValue,
|
||||
Duration.ZERO)).isNull();
|
||||
|
||||
doWithConnection(connection -> {
|
||||
assertThat(connection.get(binaryCacheKey)).isEqualTo(binaryCacheValue);
|
||||
});
|
||||
|
||||
assertThat(writer.getStatistics().getStores()).isOne();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-481
|
||||
@Test // DATAREDIS-481, DATAREDIS-1082
|
||||
public void putIfAbsentShouldNotAddEternalEntryWhenKeyAlreadyExist() {
|
||||
|
||||
doWithConnection(connection -> connection.set(binaryCacheKey, binaryCacheValue));
|
||||
|
||||
assertThat(nonLockingRedisCacheWriter(connectionFactory).putIfAbsent(CACHE_NAME, binaryCacheKey, "foo".getBytes(),
|
||||
Duration.ZERO)).isEqualTo(binaryCacheValue);
|
||||
RedisCacheWriter writer = nonLockingRedisCacheWriter(connectionFactory);
|
||||
assertThat(writer.putIfAbsent(CACHE_NAME, binaryCacheKey, "foo".getBytes(), Duration.ZERO))
|
||||
.isEqualTo(binaryCacheValue);
|
||||
|
||||
doWithConnection(connection -> {
|
||||
assertThat(connection.get(binaryCacheKey)).isEqualTo(binaryCacheValue);
|
||||
});
|
||||
|
||||
assertThat(writer.getStatistics().getStores()).isZero();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-481
|
||||
@Test // DATAREDIS-481, DATAREDIS-1082
|
||||
public void putIfAbsentShouldAddExpiringEntryWhenKeyDoesNotExist() {
|
||||
|
||||
assertThat(nonLockingRedisCacheWriter(connectionFactory).putIfAbsent(CACHE_NAME, binaryCacheKey, binaryCacheValue,
|
||||
Duration.ofSeconds(5))).isNull();
|
||||
RedisCacheWriter writer = nonLockingRedisCacheWriter(connectionFactory);
|
||||
assertThat(writer.putIfAbsent(CACHE_NAME, binaryCacheKey, binaryCacheValue, Duration.ofSeconds(5))).isNull();
|
||||
|
||||
doWithConnection(connection -> {
|
||||
assertThat(connection.ttl(binaryCacheKey)).isGreaterThan(3).isLessThan(6);
|
||||
});
|
||||
assertThat(writer.getStatistics().getStores()).isOne();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-481
|
||||
@Test // DATAREDIS-481, DATAREDIS-1082
|
||||
public void removeShouldDeleteEntry() {
|
||||
|
||||
doWithConnection(connection -> connection.set(binaryCacheKey, binaryCacheValue));
|
||||
|
||||
nonLockingRedisCacheWriter(connectionFactory).remove(CACHE_NAME, binaryCacheKey);
|
||||
RedisCacheWriter writer = nonLockingRedisCacheWriter(connectionFactory);
|
||||
writer.remove(CACHE_NAME, binaryCacheKey);
|
||||
|
||||
doWithConnection(connection -> assertThat(connection.exists(binaryCacheKey)).isFalse());
|
||||
assertThat(writer.getStatistics().getRemovals()).isOne();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-418
|
||||
@Test // DATAREDIS-418, DATAREDIS-1082
|
||||
public void cleanShouldRemoveAllKeysByPattern() {
|
||||
|
||||
doWithConnection(connection -> {
|
||||
@@ -203,13 +223,14 @@ public class DefaultRedisCacheWriterTests {
|
||||
connection.set("foo".getBytes(), "bar".getBytes());
|
||||
});
|
||||
|
||||
nonLockingRedisCacheWriter(connectionFactory).clean(CACHE_NAME,
|
||||
(CACHE_NAME + "::*").getBytes(Charset.forName("UTF-8")));
|
||||
RedisCacheWriter writer = nonLockingRedisCacheWriter(connectionFactory);
|
||||
writer.clean(CACHE_NAME, (CACHE_NAME + "::*").getBytes(Charset.forName("UTF-8")));
|
||||
|
||||
doWithConnection(connection -> {
|
||||
assertThat(connection.exists(binaryCacheKey)).isFalse();
|
||||
assertThat(connection.exists("foo".getBytes())).isTrue();
|
||||
});
|
||||
assertThat(writer.getStatistics().getRemovals()).isOne();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-481
|
||||
@@ -237,18 +258,17 @@ public class DefaultRedisCacheWriterTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-481
|
||||
@Test // DATAREDIS-481, DATAREDIS-1082
|
||||
public void lockingCacheWriterShouldWaitForLockRelease() throws InterruptedException {
|
||||
|
||||
DefaultRedisCacheWriter cw = (DefaultRedisCacheWriter) lockingRedisCacheWriter(connectionFactory);
|
||||
cw.lock(CACHE_NAME);
|
||||
DefaultRedisCacheWriter writer = (DefaultRedisCacheWriter) lockingRedisCacheWriter(connectionFactory);
|
||||
writer.lock(CACHE_NAME);
|
||||
|
||||
CountDownLatch beforeWrite = new CountDownLatch(1);
|
||||
CountDownLatch afterWrite = new CountDownLatch(1);
|
||||
|
||||
Thread th = new Thread(() -> {
|
||||
|
||||
RedisCacheWriter writer = lockingRedisCacheWriter(connectionFactory);
|
||||
beforeWrite.countDown();
|
||||
writer.put(CACHE_NAME, binaryCacheKey, binaryCacheValue, Duration.ZERO);
|
||||
afterWrite.countDown();
|
||||
@@ -265,12 +285,13 @@ public class DefaultRedisCacheWriterTests {
|
||||
assertThat(connection.exists(binaryCacheKey)).isFalse();
|
||||
});
|
||||
|
||||
cw.unlock(CACHE_NAME);
|
||||
writer.unlock(CACHE_NAME);
|
||||
|
||||
afterWrite.await();
|
||||
doWithConnection(connection -> {
|
||||
assertThat(connection.exists(binaryCacheKey)).isTrue();
|
||||
});
|
||||
assertThat(writer.getStatistics().getLockWaitDuration(TimeUnit.NANOSECONDS)).isGreaterThan(0);
|
||||
} finally {
|
||||
th.interrupt();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user