Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/suppierk/spring-boot-multilevel-cache-starter

Spring Boot multi-level caching with Redis and Caffeine
https://github.com/suppierk/spring-boot-multilevel-cache-starter

cache caching caffeine-cache microservices redis spring spring-boot two-level-cache

Last synced: 3 days ago
JSON representation

Spring Boot multi-level caching with Redis and Caffeine

Awesome Lists containing this project

README

        

# Spring Boot multi-level cache starter

Opinionated version of multi-level caching for [Spring Boot](https://spring.io/projects/spring-boot) with [Redis](https://redis.io/) as L2 (remote) cache and [Caffeine](https://github.com/ben-manes/caffeine) as L1 (local) cache with a Circuit Breaker pattern for L2 cache calls.

This version does not allow setting most of the local cache properties in favor of managing local cache expiry by itself.

[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B20864%2Fgithub.com%2FSuppieRK%2Fspring-boot-multilevel-cache-starter.svg?type=shield&issueType=license)](https://app.fossa.com/projects/custom%2B20864%2Fgithub.com%2FSuppieRK%2Fspring-boot-multilevel-cache-starter?ref=badge_shield&issueType=license)

[![SonarCloud](https://sonarcloud.io/images/project_badges/sonarcloud-orange.svg)](https://sonarcloud.io/summary/overall?id=SuppieRK_spring-boot-multilevel-cache-starter)

## Usage
### Maven
```xml

io.github.suppierk
spring-boot-multilevel-cache-starter
3.4.1.1

```

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

## Use cases

### Suitable for
- Microservices working with immutable cached entities under low latency requirements
- The goal is to not only reduce the number of calls to external service but also reduce the number of calls to Redis

### Not a good fit for
- Mutable cached entities
- Entities with short time to live (< 5 minutes)
- Cases when entities in local cache **must** outlive entities in distributed cache
- Consider using only local cache instead
- Cases when all calls to Redis must be synchronized with distributed locks

## Ideas

- Use well-known Spring primitives for implementation
- Microservices environment needs to fit the requirement of fault tolerance:
- Redis calls covered by [Resilience4j Circuit Breaker](https://resilience4j.readme.io/docs/circuitbreaker) which allows falling back to use local cache at the cost of increased latency and more calls to external services.
- Redis TTL behaves similar to `expireAfterWrite` in Caffeine which allows us to set randomized expiry time for local cache:
- This is useful to ensure that local cache entries will expire earlier for a higher chance to hit Redis instead of performing external call.
- This also implicitly reduces the load on the Redis by spreading calls to it over time.
- In the case of Redis connection errors, randomized expiry and Circuit Breaker will help to mitigate [thundering herd problem](https://en.wikipedia.org/wiki/Thundering_herd_problem).
- Expiry randomization follows the rule: `(time-to-live / 2) * (1 ± ((expiry-jitter / 100) * RNG(0, 1)))`, for example:
- If `spring.cache.multilevel.time-to-live` is `1h`
- And `spring.cache.multilevel.local.expiry-jitter` is `50` (percents)
- Then entries in local cache will expire in approximately `15-45m`:
```
(1h / 2) * (1 ± ((50 / 100) * RNG(0, 1))) ->
30m * (1 ± MAXRNG(0.5)) ->
30m * RANGE(0.5, 1.5) ->
15-45m
```

## Default configuration

```yaml
spring:
data:
redis:
host: ${HOST:localhost}
port: ${PORT:6379}
cache:
type: redis

# These properties are custom
multilevel:
# Redis properties
time-to-live: 1h
use-key-prefix: false
key-prefix: ""
topic: "cache:multilevel:topic"
# Local Caffeine cache properties
local:
max-size: 2000
expiry-jitter: 50
expiration-mode: after-create
# other valid values for expiration-mode: after-update, after-read
# Resilience4j Circuit Breaker properties for Redis
circuit-breaker:
failure-rate-threshold: 25
slow-call-rate-threshold: 25
slow-call-duration-threshold: 250ms
sliding-window-type: count_based
permitted-number-of-calls-in-half-open-state: 20
max-wait-duration-in-half-open-state: 5s
sliding-window-size: 40
minimum-number-of-calls: 10
wait-duration-in-open-state: 2500ms
```

## Honorable mentions

- [Circuit Breaker Redis Cache by gee4vee](https://github.com/gee4vee/circuit-breaker-redis-cache)
- [Multilevel cache Spring Boot starter by pig777](https://github.com/pig-mesh/multilevel-cache-spring-boot-starter)

## License
[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B20864%2Fgithub.com%2FSuppieRK%2Fspring-boot-multilevel-cache-starter.svg?type=large&issueType=license)](https://app.fossa.com/projects/custom%2B20864%2Fgithub.com%2FSuppieRK%2Fspring-boot-multilevel-cache-starter?ref=badge_large&issueType=license)