Files
spring-boot-demo/redis/docs/06-hash-ttl-and-keyspace-events.md

72 lines
3.7 KiB
Markdown

[Home](../README.md) | Prev: [@RedisHash](05-redis-hash.md) | Next: [Pub/Sub](07-pub-sub.md)
# 6. Hash TTL and keyspace events
`@RedisHash(value = "session", timeToLive = 2)` gives every saved [Session](../src/main/java/com/ankurm/redis/hash/Session.java)
a two-second TTL. Redis will delete the hash when the time is up. The question is what happens to
everything Spring Data Redis wrote *around* the hash: the id in the `session` set, the
`session:user:ankur` index, the `session:s1:idx` bookkeeping set (see [chapter 5](05-redis-hash.md)).
## With Boot's defaults: nothing
Boot enables Redis repositories with `enableKeyspaceEvents = OFF`
([16-serializers-javap.txt](output/16-serializers-javap.txt), "Defaults of @EnableRedisRepositories").
Redis expires the hash and nobody tells the application
([09-hash-ttl-events-off.txt](output/09-hash-ttl-events-off.txt)):
```
$ redis-cli -p 6390 KEYS '*' | sort
session
session:s1:idx
session:user:ankur
$ redis-cli -p 6390 SMEMBERS session | sort
s1
...
sessions.findById("s1") = Optional.empty
sessions.count() = 1
```
`findById` is right (there is no hash), `count()` is wrong (the id is still in the set), and
`session:user:ankur` still lists an id that resolves to nothing. On a busy system this accumulates.
## With ON_STARTUP: cleanup
`@EnableRedisRepositories(enableKeyspaceEvents = ON_STARTUP)` changes three things
([10-hash-ttl-events-on.txt](output/10-hash-ttl-events-on.txt), test config in
[HashTtlEventsOnTest](../src/test/java/com/ankurm/redis/HashTtlEventsOnTest.java)):
1. **The server setting.** Redis only publishes expiry notifications if `notify-keyspace-events` is set. It is empty by default, and Spring Data Redis sets it at startup (the annotation's `keyspaceNotificationsConfigParameter` defaults to `Ex`; Redis reports it back as `xE`):
```
$ redis-cli -p 6390 --no-raw CONFIG GET notify-keyspace-events
1) "notify-keyspace-events"
2) "xE"
```
Some managed Redis services restrict `CONFIG SET`; this repository does not run against one, so check yours. The `EnableKeyspaceEvents` enum also has an `ON_DEMAND` value, which is not exercised here.
2. **A phantom key.** Once a hash has expired there is nothing left to read, so Spring Data Redis writes a copy, `session:s1:phantom`, that outlives the hash by five minutes (302 seconds for a 2-second TTL). When the expiry notification arrives, Spring reads the phantom copy, removes the index entries, and deletes the phantom.
3. **An application event.** `RedisKeyExpiredEvent` is published with the deleted entity as its value, so an `@EventListener` can react (audit, cleanup, notify):
```
RedisKeyExpiredEvent: keyspace=session id=s1 value=Session[id=s1, user=ankur]
```
After it, `DBSIZE` is 0 and `count()` is 0.
## The catch
The expiry notification is delivered over Pub/Sub, which is fire-and-forget: a subscriber that is
not connected when the message is published never sees it ([chapter 7](07-pub-sub.md) shows that
with a stopped listener). If your application is down when a session expires, or the connection
drops, the cleanup for that entry is not guaranteed. This repository does not test a missed notification. If exactness matters, do not build correctness on keyspace events; run a periodic job.
## Going deeper
- [HashTtlEventsOffTest](../src/test/java/com/ankurm/redis/HashTtlEventsOffTest.java)
- [Redis keyspace notifications](https://redis.io/docs/latest/develop/pubsub/keyspace-notifications/)
- [Spring Data Redis: Expirations and Time To Live](https://docs.spring.io/spring-data/redis/reference/redis/redis-repositories/expirations.html)
[Home](../README.md) | Prev: [@RedisHash](05-redis-hash.md) | Next: [Pub/Sub](07-pub-sub.md)