RedisにてKeyにexpireで指定した時間を過ぎた時に,そのKeyのValueが欲しいということがあった.
Redis 2.8以上であれば,keyspace notificationsという仕組みがあり,Redis データセットに対するなんらかの変更イベントをPub/Subチャンネルで受け取ることが可能である.

詳しくは上記ドキュメントに書いてあるので,ここでは省略する.

notify-keyspace-eventsKEAに設定しておくと,とりあえず発生するイベント全部取れる.

$ redis-cli
config set notify-keyspace-events KEA

Redisを起動するごとに設定するのは面倒なので,/etc/redis/redis.confnotify-keyspace-events KEAの一行を追加しておくとハッピー.

subscribe __keyevent@0__:expiredとかすればdatabase 0で有効期限が切れたKeyを検知できる.

検知した際,以下のようなデータが返ってくる.

{
    'type': 'pmessage',
    'pattern': b'__keyevent@0__:expired',
    'channel': b'__keyevent@0__:expired',
    'data': 'keyname'
}

ちなみにsubscribe __keyspace@0__:{キー}とすれば特定のKeyに対して発生したイベントを検知できる.

ここで,注意するのが,返ってくるのはKeyだけでValueは返ってこず,当然既に削除されているのでValueを確認する術はない.
あまりスマートな方法とは言えないが,Valueも欲しい場合はSET shadowkey:keyname "" EX 100みたいなことをして,実際のKeyにはexpireを設定せず,別途expire専用のKeyを保存する.

$ redis-cli
SET keyname "value"
SET shadowkey:keyname "" EX 100
// shadowkey:keynameが期限切れになった
GET keyname
DEL keyname

Pythonで書くと以下のような感じになる.

import redis


r = redis.Redis(host='localhost', port=6379, db=0)

pubsub = r.pubsub()
pubsub.psubscribe('__keyevent@0__:expired')
for msg in pubsub.listen():
    if msg['pattern'] == b'__keyevent@0__:expired':
        """
        {
        'type': 'pmessage',
        'pattern': b'__keyevent@0__:expired',
        'channel': b'__keyevent@0__:expired',
        'data': 'shadowkey:keyname'
        }
        """
        k = msg['data'].decode("utf-8").split(":")[1]
        value = r.hget(k, 'somevalue').decode('utf-8')
        # 何か処理をしてKeyを削除する
        r.delete(k)
redis