Class: GCRA::RedisStore

Inherits:
Object
  • Object
show all
Defined in:
lib/gcra/redis_store.rb

Overview

Redis store, expects all timestamps and durations to be integers with nanoseconds since epoch.

Constant Summary collapse

CAS_SCRIPT =
"local v = redis.call('get', KEYS[1])\nif v == false then\nreturn redis.error_reply(\"key does not exist\")\nend\nif v ~= ARGV[1] then\nreturn 0\nend\nif ARGV[3] ~= \"0\" then\nredis.call('psetex', KEYS[1], ARGV[3], ARGV[2])\nelse\nredis.call('set', KEYS[1], ARGV[2])\nend\nreturn 1\n".freeze
CAS_SCRIPT_MISSING_KEY_RESPONSE =
'key does not exist'.freeze

Instance Method Summary collapse

Constructor Details

#initialize(redis, key_prefix) ⇒ RedisStore


21
22
23
24
# File 'lib/gcra/redis_store.rb', line 21

def initialize(redis, key_prefix)
  @redis = redis
  @key_prefix = key_prefix
end

Instance Method Details

#compare_and_set_with_ttl(key, old_value, new_value, ttl_nano) ⇒ Object

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.


57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/gcra/redis_store.rb', line 57

def compare_and_set_with_ttl(key, old_value, new_value, ttl_nano)
  full_key = @key_prefix + key
  begin
    ttl_milli = ttl_nano / 1_000_000
    swapped = @redis.eval(CAS_SCRIPT, keys: [full_key], argv: [old_value, new_value, ttl_milli])
  rescue Redis::CommandError => e
    if e.message == CAS_SCRIPT_MISSING_KEY_RESPONSE
      return false
    end
    raise
  end

  return swapped == 1
end

#get_with_time(key) ⇒ Object

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.


28
29
30
31
32
33
34
35
36
37
38
# File 'lib/gcra/redis_store.rb', line 28

def get_with_time(key)
  time_response = @redis.time # returns tuple (seconds since epoch, microseconds)
  # Convert tuple to nanoseconds
  time = (time_response[0] * 1_000_000 + time_response[1]) * 1_000
  value = @redis.get(@key_prefix + key)
  if value != nil
    value = value.to_i
  end

  return value, time
end

#set_if_not_exists_with_ttl(key, value, ttl_nano) ⇒ Object

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). The operations are not performed atomically.


42
43
44
45
46
47
48
49
50
51
52
# File 'lib/gcra/redis_store.rb', line 42

def set_if_not_exists_with_ttl(key, value, ttl_nano)
  full_key = @key_prefix + key
  did_set = @redis.setnx(full_key, value)

  if did_set && ttl_nano > 0
    ttl_milli = ttl_nano / 1_000_000
    @redis.pexpire(full_key, ttl_milli)
  end

  return did_set
end