Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set expiration atomically #252

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions lib/redis/base_object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,27 @@ def redis

def set_expiration
if !@options[:expiration].nil?
redis.expire(@key, @options[:expiration]) if redis.ttl(@key) < 0
redis.expire(@key, @options[:expiration])
elsif !@options[:expireat].nil?
expireat = @options[:expireat]
at = expireat.respond_to?(:call) ? expireat.call.to_i : expireat.to_i
redis.expireat(@key, at) if redis.ttl(@key) < 0
redis.expireat(@key, at)
end
end

def allow_expiration(&block)
result = block.call
set_expiration
result
def allow_expiration
expiration_set = false
result =
redis.multi do
yield
expiration_set = set_expiration
end
# Nested calls to multi/pipelined return `nil`,
# return value should be handled by outer call to multi/pipelined.
return if result.nil?

result.pop if expiration_set
result.size == 1 ? result.first : result
end

def to_json(*args)
Expand Down
40 changes: 13 additions & 27 deletions lib/redis/counter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ def initialize(key, *args)
# with a parent and starting over (for example, restarting a game and
# disconnecting all players).
def reset(to=options[:start])
allow_expiration do
redis.set key, to.to_i
true # hack for redis-rb regression
end
allow_expiration { redis.set(key, to.to_i) }
true # hack for redis-rb regression
end

# Reset the counter to its starting value, and return previous value.
Expand All @@ -45,13 +43,9 @@ def value
alias_method :get, :value

def value=(val)
allow_expiration do
if val.nil?
delete
else
redis.set key, val
end
end
return delete if val.nil?

allow_expiration { redis.set(key, val) }
end
alias_method :set, :value=

Expand All @@ -66,10 +60,8 @@ def to_f
# counter will automatically be decremented to its previous value. This
# method is aliased as incr() for brevity.
def increment(by=1, &block)
allow_expiration do
val = redis.incrby(key, by).to_i
block_given? ? rewindable_block(:decrement, by, val, &block) : val
end
val = allow_expiration { redis.incrby(key, by) }.to_i
block_given? ? rewindable_block(:decrement, by, val, &block) : val
end
alias_method :incr, :increment
alias_method :incrby, :increment
Expand All @@ -80,30 +72,24 @@ def increment(by=1, &block)
# counter will automatically be incremented to its previous value. This
# method is aliased as decr() for brevity.
def decrement(by=1, &block)
allow_expiration do
val = redis.decrby(key, by).to_i
block_given? ? rewindable_block(:increment, by, val, &block) : val
end
val = allow_expiration { redis.decrby(key, by) }.to_i
block_given? ? rewindable_block(:increment, by, val, &block) : val
end
alias_method :decr, :decrement
alias_method :decrby, :decrement

# Increment a floating point counter atomically.
# Redis uses separate API's to interact with integers vs floats.
def incrbyfloat(by=1.0, &block)
allow_expiration do
val = redis.incrbyfloat(key, by).to_f
block_given? ? rewindable_block(:decrbyfloat, by, val, &block) : val
end
val = allow_expiration { redis.incrbyfloat(key, by) }.to_f
block_given? ? rewindable_block(:decrbyfloat, by, val, &block) : val
end

# Decrement a floating point counter atomically.
# Redis uses separate API's to interact with integers vs floats.
def decrbyfloat(by=1.0, &block)
allow_expiration do
val = redis.incrbyfloat(key, -by).to_f
block_given? ? rewindable_block(:incrbyfloat, by, val, &block) : val
end
val = allow_expiration { redis.incrbyfloat(key, -by) }.to_f
block_given? ? rewindable_block(:incrbyfloat, by, val, &block) : val
end

##
Expand Down
24 changes: 4 additions & 20 deletions lib/redis/hash_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@ def initialize(key, *args)

# Redis: HSET
def store(field, value)
allow_expiration do
redis.hset(key, field, marshal(value, options[:marshal_keys][field]))
end
allow_expiration { redis.hset(key, field, marshal(value, options[:marshal_keys][field])) }
end
alias_method :[]=, :store

# Redis: HGET
def hget(field)
unmarshal redis.hget(key, field), options[:marshal_keys][field]
unmarshal(redis.hget(key, field), options[:marshal_keys][field])
end
alias_method :get, :hget
alias_method :[], :hget
Expand Down Expand Up @@ -134,14 +132,7 @@ def bulk_values(*keys)

# Increment value by integer at field. Redis: HINCRBY
def incrby(field, by=1)
allow_expiration do
ret = redis.hincrby(key, field, by)
unless ret.is_a? Array
ret.to_i
else
nil
end
end
allow_expiration { redis.hincrby(key, field, by) }.to_i
end
alias_method :incr, :incrby

Expand All @@ -153,14 +144,7 @@ def decrby(field, by=1)

# Increment value by float at field. Redis: HINCRBYFLOAT
def incrbyfloat(field, by=1.0)
allow_expiration do
ret = redis.hincrbyfloat(key, field, by)
unless ret.is_a? Array
ret.to_f
else
nil
end
end
allow_expiration { redis.hincrbyfloat(key, field, by) }.to_f
end

# Decrement value by float at field. Redis: HINCRBYFLOAT
Expand Down
56 changes: 27 additions & 29 deletions lib/redis/list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,30 @@ def <<(value)
end

# Add a member before or after pivot in the list. Redis: LINSERT
def insert(where,pivot,value)
allow_expiration do
redis.linsert(key,where,marshal(pivot),marshal(value))
end
def insert(where, pivot, value)
allow_expiration { redis.linsert(key, where, marshal(pivot), marshal(value)) }
end

# Add a member to the end of the list. Redis: RPUSH
def push(*values)
allow_expiration do
count = redis.rpush(key, values.map{|v| marshal(v) })
redis.ltrim(key, -options[:maxlength], -1) if options[:maxlength]
count
end
count, =
allow_expiration do
redis.rpush(key, values.map { |v| marshal(v) })
redis.ltrim(key, -options[:maxlength], -1) if options[:maxlength]
end
count
end

# Remove a member from the end of the list. Redis: RPOP
def pop(n=nil)
if n
result, = redis.multi do
return unmarshal(redis.rpop(key)) unless n

result, =
redis.multi do
redis.lrange(key, -n, -1)
redis.ltrim(key, 0, -n - 1)
end
unmarshal result
else
unmarshal redis.rpop(key)
end
unmarshal(result)
end

# Atomically pops a value from one list, pushes to another and returns the
Expand All @@ -55,24 +53,26 @@ def rpoplpush(destination)

# Add a member to the start of the list. Redis: LPUSH
def unshift(*values)
allow_expiration do
count = redis.lpush(key, values.map{|v| marshal(v) })
redis.ltrim(key, 0, options[:maxlength] - 1) if options[:maxlength]
count
end
count, =
allow_expiration do
redis.multi do
redis.lpush(key, values.map { |v| marshal(v) })
redis.ltrim(key, 0, options[:maxlength] - 1) if options[:maxlength]
end
end
count
end

# Remove a member from the start of the list. Redis: LPOP
def shift(n=nil)
if n
result, = redis.multi do
return unmarshal(redis.lpop(key)) unless n

result, =
redis.multi do
redis.lrange(key, 0, n - 1)
redis.ltrim(key, n, -1)
end
unmarshal result
else
unmarshal redis.lpop(key)
end
unmarshal(result)
end

# Return all values in the list. Redis: LRANGE(0,-1)
Expand Down Expand Up @@ -103,9 +103,7 @@ def [](index, length=nil)

# Same functionality as Ruby arrays.
def []=(index, value)
allow_expiration do
redis.lset(key, index, marshal(value))
end
allow_expiration { redis.lset(key, index, marshal(value)) }
end

# Delete the element(s) from the list that match name. If count is specified,
Expand Down
12 changes: 5 additions & 7 deletions lib/redis/set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ def <<(value)
# Add the specified value to the set only if it does not exist already.
# Redis: SADD
def add(value)
allow_expiration do
redis.sadd(key, marshal(value)) if value.nil? || !Array(value).empty?
end
return unless value.nil? || !Array(value).empty?

allow_expiration { redis.sadd(key, marshal(value)) }
end

# Remove and return a random member. Redis: SPOP
Expand All @@ -32,15 +32,13 @@ def randmember(count = nil)
# Adds the specified values to the set. Only works on redis > 2.4
# Redis: SADD
def merge(*values)
allow_expiration do
redis.sadd(key, values.flatten.map{|v| marshal(v)})
end
allow_expiration { redis.sadd(key, values.flatten.map { |v| marshal(v) }) }
end

# Return all members in the set. Redis: SMEMBERS
def members
vals = redis.smembers(key)
vals.nil? ? [] : vals.map{|v| unmarshal(v) }
vals.nil? ? [] : vals.map { |v| unmarshal(v) }
end
alias_method :get, :members
alias_method :value, :members
Expand Down
30 changes: 10 additions & 20 deletions lib/redis/sorted_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ def []=(member, score)
# arguments to this are flipped; the member comes first rather than
# the score, since the member is the unique item (not the score).
def add(member, score)
allow_expiration do
redis.zadd(key, score, marshal(member))
end
allow_expiration { redis.zadd(key, score, marshal(member)) }
end

# Add a list of members and their corresponding value (or a hash mapping
Expand All @@ -27,7 +25,7 @@ def add(member, score)
# item (not the score).
def merge(values)
allow_expiration do
vals = values.map{|v,s| [s, marshal(v)] }
vals = values.map { |v, s| [s, marshal(v)] }
redis.zadd(key, vals)
end
end
Expand Down Expand Up @@ -117,7 +115,7 @@ def rangebyscore(min, max, options={})
options[:offset] || options[:limit] || options[:count]
args[:with_scores] = true if options[:withscores] || options[:with_scores]

redis.zrangebyscore(key, min, max, args).map{|v| unmarshal(v) }
redis.zrangebyscore(key, min, max, args).map { |v| unmarshal(v) }
end

# Returns all the elements in the sorted set at key with a score between max and min
Expand Down Expand Up @@ -153,9 +151,7 @@ def remrangebyscore(min, max)

# Delete the value from the set. Redis: ZREM
def delete(value)
allow_expiration do
redis.zrem(key, marshal(value))
end
allow_expiration { redis.zrem(key, marshal(value)) }
end

# Delete element if it matches block
Expand All @@ -173,18 +169,14 @@ def delete_if(&block)
# Increment the rank of that member atomically and return the new value. This
# method is aliased as incr() for brevity. Redis: ZINCRBY
def increment(member, by=1)
allow_expiration do
zincrby(member, by)
end
allow_expiration { zincrby(member, by) }.to_i
end
alias_method :incr, :increment
alias_method :incrby, :increment

# Convenience to calling increment() with a negative number.
def decrement(member, by=1)
allow_expiration do
zincrby(member, -by)
end
allow_expiration { zincrby(member, -by) }.to_i
end
alias_method :decr, :decrement
alias_method :decrby, :decrement
Expand All @@ -206,9 +198,8 @@ def intersection(*sets)

redis.multi do
interstore(temp_key, *sets)
redis.expire(temp_key, 1)

result = redis.zrange(temp_key, 0, -1)
redis.del(temp_key)
end

result.value
Expand Down Expand Up @@ -243,9 +234,8 @@ def union(*sets)

redis.multi do
unionstore(temp_key, *sets)
redis.expire(temp_key, 1)

result = redis.zrange(temp_key, 0, -1)
redis.del(temp_key)
end

result.value
Expand All @@ -254,7 +244,7 @@ def union(*sets)
alias_method :+, :union

# Calculate the union and store it in Redis as +name+. Returns the number
# of elements in the stored union. Redis: SUNIONSTORE
# of elements in the stored union. Redis: ZUNIONSTORE
def unionstore(name, *sets)
allow_expiration do
opts = sets.last.is_a?(Hash) ? sets.pop : {}
Expand Down Expand Up @@ -319,7 +309,7 @@ def keys_from_objects(sets)
end

def zincrby(member, by)
redis.zincrby(key, by, marshal(member)).to_i
redis.zincrby(key, by, marshal(member))
end
end
end
Loading