|
| 1 | += Redis Cache |
| 2 | +:linkattrs: |
| 3 | +:project-owner: redis-field-engineering |
| 4 | +:project-name: redis-cache-java |
| 5 | +:project-group: com.redis |
| 6 | +:project-version: 0.1.2 |
| 7 | +:project-url: https://github.com/{project-owner}/{project-name} |
| 8 | +:product-name: Redis Cache |
| 9 | +:codecov-token: 2cAc3dgZRA |
| 10 | +:artifact-id: redis-cache-core |
| 11 | +:imagesdir: .github/media |
| 12 | + |
| 13 | +image:https://github.com/{project-owner}/{project-name}/actions/workflows/early-access.yml/badge.svg["Build Status", link="https://github.com/{project-owner}/{project-name}/actions/workflows/early-access.yml"] |
| 14 | +image:https://codecov.io/gh/{project-owner}/{project-name}/graph/badge.svg?token={codecov-token}["Coverage", link="https://codecov.io/gh/{project-owner}/{project-name}"] |
| 15 | + |
| 16 | +Redis Cache is a cache abstraction for the Java ecosystem that leverages enterprise Redis features like indexing and query. |
| 17 | +It provides an implementation of Spring Framework's https://docs.spring.io/spring-framework/reference/6.1/integration.html#cache[Cache Abstraction]. |
| 18 | + |
| 19 | +== Usage |
| 20 | + |
| 21 | +=== Dependency |
| 22 | + |
| 23 | +.Maven |
| 24 | +[source,xml] |
| 25 | +[subs="verbatim,attributes"] |
| 26 | +---- |
| 27 | +<dependency> |
| 28 | + <groupId>{project-group}</groupId> |
| 29 | + <artifactId>{artifact-id}</artifactId> |
| 30 | + <version>{project-version}</version> |
| 31 | +</dependency> |
| 32 | +---- |
| 33 | + |
| 34 | +.Gradle |
| 35 | +[source,groovy] |
| 36 | +[subs="attributes"] |
| 37 | +---- |
| 38 | +dependencies { |
| 39 | + implementation '{project-group}:{artifact-id}:{project-version}' |
| 40 | +} |
| 41 | +---- |
| 42 | + |
| 43 | +=== Setup |
| 44 | + |
| 45 | +To use {product-name} as a backing implementation, add `RedisCacheManager` to your configuration as follows: |
| 46 | + |
| 47 | +[source,java] |
| 48 | +----- |
| 49 | +@Bean |
| 50 | +public RedisCacheManager cacheManager(RedisModulesClient client) { |
| 51 | + return RedisCacheManager.create(client); |
| 52 | +} |
| 53 | +----- |
| 54 | + |
| 55 | +`RedisCacheManager` behavior can be configured with `RedisCacheManagerBuilder`, letting you set the default `RedisCacheConfiguration` and predefined caches. |
| 56 | + |
| 57 | +[source,java] |
| 58 | +----- |
| 59 | +RedisCacheManager cacheManager = RedisCacheManager.builder(client) |
| 60 | + .defaults(RedisCacheConfiguration.defaultConfig()) |
| 61 | + .configuration("hashCache", RedisCacheConfiguration.defaultConfig().hash()) |
| 62 | + .configuration("jsonCache", RedisCacheConfiguration.defaultConfig().json()) |
| 63 | + .configuration("stringCache", RedisCacheConfiguration.defaultConfig().string()) |
| 64 | + .build(); |
| 65 | +----- |
| 66 | + |
| 67 | +As shown in the preceding example, `RedisCacheManager` allows custom configuration on a per-cache basis. |
| 68 | + |
| 69 | +The behavior of `RedisCache` created by `RedisCacheManager` is defined with `RedisCacheConfiguration`. |
| 70 | + |
| 71 | +=== Configuration |
| 72 | + |
| 73 | +The `RedisCacheConfiguration` object is the entry point to configure a Redis cache and enable its features. |
| 74 | + |
| 75 | +==== Key Function |
| 76 | + |
| 77 | +Use `keyFunction(KeyFunction function)` to set the `KeyFunction` used to compute Redis keys. |
| 78 | + |
| 79 | +Default is `KeyFunction.SIMPLE` which produces keys in the form `<cache>:<key>`. |
| 80 | + |
| 81 | +==== Key Expiration |
| 82 | + |
| 83 | +To enable entry time-to-live (TTL) use `entryTtl(Duration duration)` or `entryTtl(TtlFunction function)`. |
| 84 | + |
| 85 | +Default is `TtlFunction.PERSISTENT` which does not expire keys. |
| 86 | + |
| 87 | +==== Redis Data Type |
| 88 | + |
| 89 | +Use `redisType(RedisType type)` to change the type of data-structure backing the cache. |
| 90 | + |
| 91 | +Possible values are `HASH`, `STRING`, and `JSON`. |
| 92 | + |
| 93 | +Default is `HASH`. |
| 94 | + |
| 95 | +Each type has a corresponding value mapper which can be overriden: |
| 96 | + |
| 97 | +* `HASH`: `hashMapper(RedisHashMapper mapper)` |
| 98 | +* `STRING`: `stringMapper(RedisStringMapper mapper)` |
| 99 | +* `JSON`: `jsonMapper(RedisStringMapper mapper)` |
| 100 | + |
| 101 | +==== Client-Side Caching |
| 102 | + |
| 103 | +Client-side caching (also known as local or near caching) can be enabled by setting `localCache(Map<String, Object> cache)`. |
| 104 | + |
| 105 | +For example with a simple `java.util.Map` implementation: `localCache(new HashMap<>())`. |
| 106 | + |
| 107 | +For more control over the behavior of the local cache it is recommended to use an in-memory cache implementation like https://github.com/ben-manes/caffeine[Caffeine]: |
| 108 | + |
| 109 | +[source,java] |
| 110 | +---- |
| 111 | +Cache<String, Object> localCache = Caffeine.newBuilder() |
| 112 | + .maximumSize(10000) |
| 113 | + .expireAfterWrite(Duration.ofMinutes(5)) |
| 114 | + .build(); |
| 115 | +return RedisCacheConfiguration.defaultConfig().localCache(localCache.asMap()); |
| 116 | +---- |
| 117 | + |
| 118 | +See the https://github.com/ben-manes/caffeine/wiki[Caffeine Documentation] for more configuration options. |
| 119 | + |
| 120 | +==== Metrics |
| 121 | + |
| 122 | +{product-name} uses Micrometer to publish metrics. |
| 123 | +To enable metrics, set a `MeterRegistry` in your `RedisCacheConfiguration`: |
| 124 | + |
| 125 | +[source,java] |
| 126 | +---- |
| 127 | +RedisCacheConfiguration.defaultConfig().meterRegistry(registry); |
| 128 | +---- |
| 129 | + |
| 130 | +The following metrics are published: |
| 131 | + |
| 132 | +[cols="2,2,1,5"] |
| 133 | +|=== |
| 134 | +| Name | Tags | Type | Description |
| 135 | + |
| 136 | +| `cache.gets` |
| 137 | +| `result=hit\|miss` |
| 138 | +| Counter |
| 139 | +| The number of times cache lookup methods have returned a cached (hit) or uncached (miss) value. |
| 140 | + |
| 141 | +| `cache.puts` |
| 142 | +| |
| 143 | +| Counter |
| 144 | +| The number of entries added to the cache. |
| 145 | + |
| 146 | +| `cache.evictions` |
| 147 | +| |
| 148 | +| Counter |
| 149 | +| The number of times the cache was evicted. |
| 150 | + |
| 151 | +| `cache.gets.latency` |
| 152 | +| |
| 153 | +| Timer |
| 154 | +| Cache get latency |
| 155 | + |
| 156 | +| `cache.puts.latency` |
| 157 | +| |
| 158 | +| Timer |
| 159 | +| Cache put latency |
| 160 | + |
| 161 | +| `cache.evictions.latency` |
| 162 | +| |
| 163 | +| Timer |
| 164 | +| Cache eviction latency |
| 165 | + |
| 166 | +| `cache.local.gets` |
| 167 | +| `result=hit\|miss` |
| 168 | +| Counter |
| 169 | +| The number of times local cache lookup methods have returned a cached (hit) or uncached (miss) value. |
| 170 | + |
| 171 | +| `cache.local.evictions` |
| 172 | +| |
| 173 | +| Counter |
| 174 | +| The number of times the local cache was evicted. |
| 175 | + |
| 176 | + |
| 177 | +|=== |
| 178 | + |
| 179 | +NOTE: All metrics expose their corresponding cache name as a tag: `name=<cache>`. |
| 180 | + |
| 181 | +== Quick Start |
| 182 | + |
| 183 | +To understand how {product-name} works, it's best to try it for yourself. |
| 184 | + |
| 185 | +This example showcases a Spring Boot application using {product-name}. |
| 186 | + |
| 187 | +First, clone this git repository: |
| 188 | + |
| 189 | +[source,console,subs="verbatim,attributes"] |
| 190 | +---- |
| 191 | +git clone {project-url}.git |
| 192 | +cd {project-name} |
| 193 | +---- |
| 194 | + |
| 195 | +Next, register and create an API read-access token at https://developer.themoviedb.org/docs/getting-started[themoviedb.org]. |
| 196 | + |
| 197 | +Set the following environment variable with the token: |
| 198 | + |
| 199 | +[source,console] |
| 200 | +---- |
| 201 | +export TMDB_TOKEN=<your API read-access token> |
| 202 | +---- |
| 203 | + |
| 204 | +Finally use Docker Compose to launch containers for Redis and the {product-name} demo app instance: |
| 205 | + |
| 206 | +[source,console] |
| 207 | +---- |
| 208 | +docker compose up |
| 209 | +---- |
| 210 | + |
| 211 | +You can then access the demo at http://localhost:8080/[localhost:8080]. |
| 212 | + |
| 213 | +As you click around on the different pages, notice how the response time improves after the first time you request a page. |
| 214 | + |
| 215 | +image:redis-cache-demo.png[] |
| 216 | + |
| 217 | +Open another browser window and access Grafana at http://localhost:3000/[localhost:3000]. |
| 218 | +Use username/password `admin`/`admin` to log in. |
| 219 | +You can skip changing password. |
| 220 | + |
| 221 | +Arrange your browser windows with the demo app on the left and Grafana on the right: |
| 222 | + |
| 223 | +image:redis-cache-demo-grafana.png[] |
| 224 | + |
| 225 | +Notice the HTTP response time decreasing with cache hits, and increasing with API requests. |
| 226 | + |
| 227 | +Now click on the http://localhost:8080/movies/search[Search] link to search the cache, for example with keyword `corleone`. |
| 228 | + |
| 229 | +Notice how quickly search results are returned: the search feature is powered by Redis. |
| 230 | + |
| 231 | +Search response time can be visualized with the panel at the bottom of the Grafana dashboard. |
| 232 | + |
| 233 | +image:redis-cache-demo-search.png[] |
| 234 | + |
0 commit comments