Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01B38FGKKam5SCGgwgduVAh3
72 lines
3.7 KiB
Markdown
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)
|