Skip to content

Commit

Permalink
fix: Possible infinite loop during eviction of entities
Browse files Browse the repository at this point in the history
  • Loading branch information
SuppieRK committed Aug 18, 2021
1 parent 8ee0b2e commit baa92f7
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 9 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
# Changelog

All notable changes to this project will be documented in this file.

The format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.1]

### Fixed
- Possible infinite loop during eviction of entities

## [1.0.0]

### Added
- Initial implementation

[1.0.1]: https://github.com/SuppieRK/spring-boot-multilevel-cache-starter/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/SuppieRK/spring-boot-multilevel-cache-starter/compare/6a187283...v1.0.0
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ This version does not allow setting most of the local cache properties in favor
<dependency>
<groupId>io.github.suppierk</groupId>
<artifactId>spring-boot-multilevel-cache-starter</artifactId>
<version>1.0.0</version>
<version>1.0.1</version>
</dependency>
```

### Gradle
```groovy
implementation 'io.github.suppierk:spring-boot-multilevel-cache-starter:1.0.0'
implementation 'io.github.suppierk:spring-boot-multilevel-cache-starter:1.0.1'
```

## Default configuration
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#

GROUP=io.github.suppierk
VERSION_NAME=1.0.0
VERSION_NAME=1.0.1

POM_ARTIFACT_ID=spring-boot-multilevel-cache-starter
POM_NAME=Spring Boot Multilevel Cache Starter
Expand Down
25 changes: 23 additions & 2 deletions src/main/java/io/github/suppie/spring/cache/MultiLevelCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,22 @@ public ValueWrapper putIfAbsent(@NonNull Object key, @Nullable Object value) {
*/
@Override
public void evict(@NonNull Object key) {
sendViaRedis(localEvict(key));
}

/**
* Local copy of {@link #evict(Object)} method for Redis Pub/Sub listener to avoid infinite
* message loop
*
* @param key the key whose mapping is to be removed from the cache
* @return computed key for entry to evict
* @see #evict(Object)
*/
String localEvict(@NonNull Object key) {
final String localKey = convertKey(key);
localCache.invalidate(localKey);
callRedis(() -> super.evict(key));
sendViaRedis(localKey);
return localKey;
}

/**
Expand Down Expand Up @@ -329,9 +341,18 @@ public boolean evictIfPresent(@NonNull Object key) {
*/
@Override
public void clear() {
localClear();
sendViaRedis(null);
}

/**
* Local copy of {@link #clear()} method for Redis Pub/Sub listener to avoid infinite message loop
*
* @see #clear()
*/
void localClear() {
localCache.invalidateAll();
callRedis(super::clear);
sendViaRedis(null);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.Cache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.MessageListener;
Expand Down Expand Up @@ -220,14 +219,19 @@ private static MessageListener createMessageListener(

if (!StringUtils.hasText(cacheName)) return;

Cache cache = cacheManager.getCache(cacheName);
MultiLevelCache cache = (MultiLevelCache) cacheManager.getCache(cacheName);

if (cache == null) return;

log.trace("Received Redis message to evict key {} from cache {}", entryKey, cacheName);

if (entryKey == null) cache.clear();
else cache.evict(entryKey);
if (entryKey == null) cache.localClear();
else cache.localEvict(entryKey);
} catch (ClassCastException e) {
log.error(
"Cannot cast cache instance returned by cache manager to "
+ MultiLevelCache.class.getName(),
e);
} catch (Exception e) {
log.debug("Unknown Redis message", e);
}
Expand Down

0 comments on commit baa92f7

Please sign in to comment.