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

3.7 KiB

Home | Prev: @RedisHash | Next: Pub/Sub

6. Hash TTL and keyspace events

@RedisHash(value = "session", timeToLive = 2) gives every saved Session 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).

With Boot's defaults: nothing

Boot enables Redis repositories with enableKeyspaceEvents = OFF (16-serializers-javap.txt, "Defaults of @EnableRedisRepositories"). Redis expires the hash and nobody tells the application (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, test config in HashTtlEventsOnTest):

  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 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

Home | Prev: @RedisHash | Next: Pub/Sub