Class: Rotary::Storage::Redis

Inherits:
Object
  • Object
show all
Defined in:
lib/rotary/storage/redis.rb

Defined Under Namespace

Classes: Retry

Constant Summary collapse

DEFAULT_PREFIX =
'rotary'.freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection:, ttl:, serializer:, prefix: DEFAULT_PREFIX) ⇒ Redis

Returns a new instance of Redis.



16
17
18
19
20
21
22
# File 'lib/rotary/storage/redis.rb', line 16

def initialize(connection:, ttl:, serializer:, prefix: DEFAULT_PREFIX)
  @redis = connection
  @ttl = ttl # in seconds
  @prefix = "#{prefix}::"
  @serializer = serializer
  @pool_list = "#{@prefix}pool"
end

Class Method Details

.default_connectionObject



12
13
14
# File 'lib/rotary/storage/redis.rb', line 12

def self.default_connection
  ::Redis.new
end

Instance Method Details

#clean_older_than(n) ⇒ Object

Removes sessions, where ttl is bigger than threshold n.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/rotary/storage/redis.rb', line 62

def clean_older_than(n)
  # It doesn't have to happen atomically.
  # New session will be lpush'ed, we can easily check only
  # N sessions from the right.
  size.times do
    serialized_session = @redis.rpop(@pool_list)

    # We have no sessions left. It can happen.
    break unless serialized_session

    key = ttl_key(serialized_session)
    ttl_marker = @redis.ttl(key)

    # When key doesn't exist
    #   redis <= 2.6 returns -1
    #   redis >= 2.8 returns -2
    no_key = [-1, -2].include?(ttl_marker)
    next if no_key

    old = @ttl ? ttl_marker < (@ttl - n) : false
    if old
      # delete ttl key
      @redis.del(key)
      # and execute the block with session as arg
      session = @serializer.load(serialized_session)
      yield(session) if block_given?
    else
      # push back from the left side
      @redis.lpush(@pool_list, serialized_session)
    end
  end
end

#clearObject



57
58
59
# File 'lib/rotary/storage/redis.rb', line 57

def clear
  @redis.del(@pool_list)
end

#popObject



38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/rotary/storage/redis.rb', line 38

def pop
  serialized = @redis.rpop(@pool_list)
  obj = serialized ? @serializer.load(serialized) : nil
  return obj unless @ttl
  if obj
    # TTL-only logic below
    key = ttl_key(serialized)
    raise Retry unless @redis.get(key)
    @redis.del(key)
  end
  obj
rescue Retry
  retry
end

#push(obj) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/rotary/storage/redis.rb', line 24

def push(obj)
  serialized = @serializer.dump(obj)
  # TODO: make lpush + set/expire transactional somehow
  @redis.lpush(@pool_list, serialized)
  if @ttl
    key = ttl_key(serialized)
    @redis.multi do
      @redis.set(key, 1)
      @redis.expire(key, @ttl)
    end
  end
  self
end

#sizeObject



53
54
55
# File 'lib/rotary/storage/redis.rb', line 53

def size
  @redis.llen(@pool_list)
end