Redis の expire の仕様

# 追記 Redis2.6.10 で試したら意図通り取得出来た。


Python で Redis を使用していた時にあたった問題。
# 2.1.3 以降では発生しないよう。


こんなコード。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from redis import Redis
client = Redis()
client.select(4)
client.flushdb()
client.rpush('a', 'x')
client.rpush('a', 'y')
client.rpush('a', 'z')
result = client.lrange('a', 0, -1)

print result # ['x', 'y', 'z']

# クリア
client.flushdb()
client.rpush('a', 'x')
client.rpush('a', 'y')
client.rpush('a', 'z')
client.expire('a', 86400)
result = client.lrange('a', 0, -1)

print result # ['x', 'y', 'z']

# クリア
client.flushdb()
client.rpush('a', 'x')
client.expire('a', 86400)
client.rpush('a', 'y')
client.expire('a', 86400)
client.rpush('a', 'z')
client.expire('a', 86400)
result = client.lrange('a', 0, -1)

print result # ['z']

一番最後の所のように毎回キーに対して expire を設定した際に、それまでの値が消えてしまった。
最初 Python のライブラリのバグかと思ったが、redis-cli でも再現した。

$ redis-cli
redis> select 4
OK
redis> rpush a x
(integer) 1
redis> rpush a y
(integer) 2
redis> rpush a z
(integer) 3
redis> lrange a 0 -1
1. "x"
2. "y"
3. "z"
redis> flushdb
OK
redis> rpush a x
(integer) 1
redis> rpush a y
(integer) 2
redis> rpush a z
(integer) 3
redis> expire a 86400
(integer) 1
redis> lrange a 0 -1
1. "x"
2. "y"
3. "z"
redis> flushdb
OK
redis> rpush a x
(integer) 1
redis> expire a 86400
(integer) 1
redis> rpush a y
(integer) 1
redis> expire a 86400
(integer) 1
redis> rpush a z
(integer) 1
redis> expire a 86400
(integer) 1
redis> lrange a 0 -1
1. "z"

揮発性のキーに対応する値に対して修正を行うような LPUSH, LSET などの操作に対しては特別なセマンティクスがあります。基本的に揮発性のキーは書き込みの対象となった場合は破壊されます。

全データ型対応の操作 — redis 2.0.3 documentation

orz


既にキーが登録してあったら、expire を付けないように回避した。