class GCRA::RedisStore
Redis store, expects all timestamps and durations to be integers with nanoseconds since epoch.
Constants
- CAS_SCRIPT
- CAS_SCRIPT_MISSING_KEY_RESPONSE
- CAS_SHA
Digest::SHA1.hexdigest(CAS_SCRIPT)
- CONNECTED_TO_READONLY
- SCRIPT_NOT_IN_CACHE_RESPONSE
Public Class Methods
new(redis, key_prefix, options = {})
click to toggle source
# File lib/gcra/redis_store.rb, line 23 def initialize(redis, key_prefix, options = {}) @redis = redis @key_prefix = key_prefix @reconnect_on_readonly = options[:reconnect_on_readonly] || false end
Public Instance Methods
compare_and_set_with_ttl(key, old_value, new_value, ttl_nano)
click to toggle source
Atomically compare the value at key to the old value. If it matches, set it to the new value and return true. Otherwise, return false. If the key does not exist in the store, return false with no error. If the swap succeeds, update the ttl for the key atomically.
# File lib/gcra/redis_store.rb, line 67 def compare_and_set_with_ttl(key, old_value, new_value, ttl_nano) full_key = @key_prefix + key retried = false begin ttl_milli = calculate_ttl_milli(ttl_nano) swapped = @redis.evalsha(CAS_SHA, keys: [full_key], argv: [old_value, new_value, ttl_milli]) rescue Redis::CommandError => e if e.message == CAS_SCRIPT_MISSING_KEY_RESPONSE return false elsif e.message == SCRIPT_NOT_IN_CACHE_RESPONSE && !retried @redis.script('load', CAS_SCRIPT) retried = true retry elsif e.message == CONNECTED_TO_READONLY && @reconnect_on_readonly && !retried @redis.client.reconnect retried = true retry end raise end return swapped == 1 end
get_with_time(key)
click to toggle source
Returns the value of the key or nil, if it isn't in the store. Also returns the time from the Redis server, with microsecond precision.
# File lib/gcra/redis_store.rb, line 32 def get_with_time(key) time_response, value = @redis.pipelined do @redis.time # returns tuple (seconds since epoch, microseconds) @redis.get(@key_prefix + key) end # Convert tuple to nanoseconds time = (time_response[0] * 1_000_000 + time_response[1]) * 1_000 if value != nil value = value.to_i end return value, time end
set_if_not_exists_with_ttl(key, value, ttl_nano)
click to toggle source
Set the value of key only if it is not already set. Return whether the value was set. Also set the key's expiration (ttl, in seconds).
# File lib/gcra/redis_store.rb, line 48 def set_if_not_exists_with_ttl(key, value, ttl_nano) full_key = @key_prefix + key retried = false begin ttl_milli = calculate_ttl_milli(ttl_nano) @redis.set(full_key, value, nx: true, px: ttl_milli) rescue Redis::CommandError => e if e.message == CONNECTED_TO_READONLY && @reconnect_on_readonly && !retried @redis.client.reconnect retried = true retry end raise end end
Private Instance Methods
calculate_ttl_milli(ttl_nano)
click to toggle source
# File lib/gcra/redis_store.rb, line 93 def calculate_ttl_milli(ttl_nano) ttl_milli = ttl_nano / 1_000_000 # Setting 0 as expiration/ttl would result in an error. # Therefore overwrite it and use 1 if ttl_milli == 0 return 1 end return ttl_milli end