Class: ActiveSupport::Cache::RedisStore

Inherits:
Store
  • Object
show all
Defined in:
lib/active_support/cache/redis_store.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*addresses) ⇒ RedisStore

Instantiate the store.

Example:

RedisStore.new
  # => host: localhost,   port: 6379,  db: 0

RedisStore.new "example.com"
  # => host: example.com, port: 6379,  db: 0

RedisStore.new "example.com:23682"
  # => host: example.com, port: 23682, db: 0

RedisStore.new "example.com:23682/1"
  # => host: example.com, port: 23682, db: 1

RedisStore.new "example.com:23682/1/theplaylist"
  # => host: example.com, port: 23682, db: 1, namespace: theplaylist

RedisStore.new "localhost:6379/0", "localhost:6380/0"
  # => instantiate a cluster

RedisStore.new "localhost:6379/0", "localhost:6380/0", pool_size: 5, pool_timeout: 10
  # => use a ConnectionPool

RedisStore.new "localhost:6379/0", "localhost:6380/0",
  pool: ::ConnectionPool.new(size: 1, timeout: 1) { ::Redis::Store::Factory.create("localhost:6379/0") })
  # => supply an existing connection pool (e.g. for use with redis-sentinel or redis-failover)


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/active_support/cache/redis_store.rb', line 36

def initialize(*addresses)
  @options = addresses.dup.extract_options!
  addresses = addresses.map(&:dup)

  @data = if @options[:pool]
            raise "pool must be an instance of ConnectionPool" unless @options[:pool].is_a?(ConnectionPool)
            @pooled = true
            @options[:pool]
          elsif [:pool_size, :pool_timeout].any? { |key| @options.has_key?(key) }
            pool_options           = {}
            pool_options[:size]    = options[:pool_size] if options[:pool_size]
            pool_options[:timeout] = options[:pool_timeout] if options[:pool_timeout]
            @pooled = true
            ::ConnectionPool.new(pool_options) { ::Redis::Store::Factory.create(*addresses) }
          else
            ::Redis::Store::Factory.create(*addresses)
          end

  super(@options)
end

Instance Attribute Details

#dataObject (readonly)

Returns the value of attribute data.



7
8
9
# File 'lib/active_support/cache/redis_store.rb', line 7

def data
  @data
end

Instance Method Details

#clearObject

Clear all the data from the store.



212
213
214
215
216
# File 'lib/active_support/cache/redis_store.rb', line 212

def clear
  instrument(:clear, nil, nil) do
    with(&:flushdb)
  end
end

#decrement(key, amount = 1, options = {}) ⇒ Object

Decrement a key in the store

If the key doesn’t exist it will be initialized on 0. If the key exist but it isn’t a Fixnum it will be initialized on 0.

Example:

We have two objects in cache:
  counter # => 23
  rabbit  # => #<Rabbit:0x5eee6c>

cache.decrement "counter"
cache.read "counter", :raw => true      # => "22"

cache.decrement "counter", 2
cache.read "counter", :raw => true      # => "20"

cache.decrement "a counter"
cache.read "a counter", :raw => true    # => "-1"

cache.decrement "rabbit"
cache.read "rabbit", :raw => true       # => "-1"


199
200
201
202
203
204
# File 'lib/active_support/cache/redis_store.rb', line 199

def decrement(key, amount = 1, options = {})
  options = merged_options(options)
  instrument(:decrement, key, :amount => amount) do
    with{|c| c.decrby normalize_key(key, options), amount}
  end
end

#delete_matched(matcher, options = nil) ⇒ Object

Delete objects for matched keys.

Uses SCAN to iterate and collect matched keys only when both client and server supports it (Redis server >= 2.8.0, client >= 3.0.6)

Performance note: this operation can be dangerous for large production databases on Redis < 2.8.0, as it uses the Redis “KEYS” command, which is O(N) over the total number of keys in the database. Users of large Redis caches should avoid this method.

Example:

cache.delete_matched "rab*"


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/active_support/cache/redis_store.rb', line 80

def delete_matched(matcher, options = nil)
  options = merged_options(options)
  instrument(:delete_matched, matcher.inspect) do
    matcher = key_matcher(matcher, options)
    begin
      with do |store|
        supports_scan_each = store.respond_to?(:supports_redis_version?) &&
          store.supports_redis_version?("2.8.0") &&
          store.respond_to?(:scan_each)

        if supports_scan_each
          keys = store.scan_each(match: matcher).to_a
        else
          keys = store.keys(matcher)
        end

        !keys.empty? && store.del(*keys)
      end
    rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Redis::CannotConnectError
      raise if raise_errors?
      false
    end
  end
end

#exist?(name, options = nil) ⇒ Boolean

Returns:

  • (Boolean)


220
221
222
223
# File 'lib/active_support/cache/redis_store.rb', line 220

def exist?(name, options = nil)
  res = super(name, options)
  res || false
end

#expire(key, ttl) ⇒ Object



206
207
208
209
# File 'lib/active_support/cache/redis_store.rb', line 206

def expire(key, ttl)
  options = merged_options(nil)
  with { |c| c.expire normalize_key(key, options), ttl }
end

#fetch_multi(*names) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/active_support/cache/redis_store.rb', line 123

def fetch_multi(*names)
  return {} if names == []
  results = read_multi(*names)
  options = names.extract_options!
  need_writes = {}

  fetched = names.inject({}) do |memo, name|
    memo[name] = results.fetch(name) do
      value = yield name
      need_writes[name] = value
      value
    end

    memo
  end

  with do |c|
    c.multi do
      need_writes.each do |name, value|
        write(name, value, options)
      end
    end
  end

  fetched
end

#increment(key, amount = 1, options = {}) ⇒ Object

Increment a key in the store.

If the key doesn’t exist it will be initialized on 0. If the key exist but it isn’t a Fixnum it will be initialized on 0.

Example:

We have two objects in cache:
  counter # => 23
  rabbit  # => #<Rabbit:0x5eee6c>

cache.increment "counter"
cache.read "counter", :raw => true      # => "24"

cache.increment "counter", 6
cache.read "counter", :raw => true      # => "30"

cache.increment "a counter"
cache.read "a counter", :raw => true    # => "1"

cache.increment "rabbit"
cache.read "rabbit", :raw => true       # => "1"


171
172
173
174
175
176
# File 'lib/active_support/cache/redis_store.rb', line 171

def increment(key, amount = 1, options = {})
  options = merged_options(options)
  instrument(:increment, key, :amount => amount) do
    with{|c| c.incrby normalize_key(key, options), amount}
  end
end

#read_multi(*names) ⇒ Object

Reads multiple keys from the cache using a single call to the servers for all keys. Options can be passed in the last argument.

Example:

cache.read_multi "rabbit", "white-rabbit"
cache.read_multi "rabbit", "white-rabbit", :raw => true


111
112
113
114
115
116
117
118
119
120
121
# File 'lib/active_support/cache/redis_store.rb', line 111

def read_multi(*names)
  return {} if names == []
  options = names.extract_options!
  keys = names.map{|name| normalize_key(name, options)}
  values = with { |c| c.mget(*keys) }
  values.map! { |v| v.is_a?(ActiveSupport::Cache::Entry) ? v.value : v }

  result = Hash[names.zip(values)]
  result.reject!{ |k,v| v.nil? }
  result
end

#reconnectObject



237
238
239
# File 'lib/active_support/cache/redis_store.rb', line 237

def reconnect
  @data.reconnect if @data.respond_to?(:reconnect)
end

#statsObject



225
226
227
# File 'lib/active_support/cache/redis_store.rb', line 225

def stats
  with(&:info)
end

#with(&block) ⇒ Object



229
230
231
232
233
234
235
# File 'lib/active_support/cache/redis_store.rb', line 229

def with(&block)
  if defined?(@pooled) && @pooled
    @data.with(&block)
  else
    block.call(@data)
  end
end

#write(name, value, options = nil) ⇒ Object



57
58
59
60
61
62
63
64
65
66
# File 'lib/active_support/cache/redis_store.rb', line 57

def write(name, value, options = nil)
  options = merged_options(options)
  instrument(:write, name, options) do |payload|
    entry = options[:raw].present? ? value : Entry.new(value, options)
    if options[:expires_in].present? && options[:race_condition_ttl].present? && options[:raw].blank?
      options[:expires_in] = options[:expires_in].to_f + options[:race_condition_ttl].to_f
    end
    write_entry(normalize_key(name, options), entry, options)
  end
end