twitter/finagle — pull request #221 by jdanbrown (2013-11-10 23:50:36+00:00)
Title: redis: Allow expirations in the past

finagle-redis explicitly guards against past expirations, even though redis allows them. Past expirations are useful because they allow the client to avoid races, e.g. when using `expireat` to enforce an expiration time that might have already passed.

Redis behavior:
```sh
$ redis-cli set foo 42
OK
$ redis-cli expire foo -60
(integer) 1
$ redis-cli exists foo
(integer) 0

$ redis-cli set foo 42
OK
$ redis-cli pexpire foo -60000
(integer) 1
$ redis-cli exists foo
(integer) 0

$ redis-cli set foo 42
OK
$ redis-cli expireat foo 0
(integer) 1
$ redis-cli exists foo
(integer) 0

$ redis-cli set foo 42
OK
$ redis-cli pexpireat foo 0
(integer) 1
$ redis-cli exists foo
(integer) 0
```

finagle-redis behavior:
```scala
scala> import org.jboss.netty.buffer.ChannelBuffer
import org.jboss.netty.buffer.ChannelBuffer

scala> import org.jboss.netty.buffer.ChannelBuffers.wrappedBuffer
import org.jboss.netty.buffer.ChannelBuffers.wrappedBuffer

scala> def s2buf(s: String): ChannelBuffer = wrappedBuffer(s.getBytes)
s2buf: (s: String)org.jboss.netty.buffer.ChannelBuffer

scala> val redis = com.twitter.finagle.redis.Client("localhost:6379")
redis: com.twitter.finagle.redis.Client = com.twitter.finagle.redis.Client@278fea01

scala> redis.expire(s2buf("foo"), -60)
com.twitter.finagle.redis.ClientError: Seconds must be greater than 0
...

scala> redis.pExpire(s2buf("foo"), -60000)
com.twitter.finagle.redis.ClientError: Milliseconds must be greater than 0
...

scala> redis.expireAt(s2buf("foo"), 0)
com.twitter.finagle.redis.ClientError: Timestamp must be in the future
...

scala> redis.pExpireAt(s2buf("foo"), 0)
com.twitter.finagle.redis.ClientError: Timestamp must be in the future
...
```