legion-cache 1.4.2 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +26 -0
- data/README.md +152 -30
- data/legion-cache.gemspec +3 -2
- data/lib/legion/cache/async_writer.rb +88 -36
- data/lib/legion/cache/cacheable.rb +37 -36
- data/lib/legion/cache/helper.rb +14 -12
- data/lib/legion/cache/local.rb +118 -109
- data/lib/legion/cache/memcached.rb +90 -26
- data/lib/legion/cache/memory.rb +220 -104
- data/lib/legion/cache/pool.rb +32 -15
- data/lib/legion/cache/reconnector.rb +50 -11
- data/lib/legion/cache/redis.rb +146 -120
- data/lib/legion/cache/redis_hash.rb +16 -13
- data/lib/legion/cache/settings.rb +8 -0
- data/lib/legion/cache/version.rb +1 -1
- data/lib/legion/cache.rb +220 -356
- metadata +35 -3
data/lib/legion/cache/redis.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'openssl'
|
|
4
4
|
require 'redis'
|
|
5
|
+
require 'uri'
|
|
5
6
|
require 'legion/logging/helper'
|
|
6
7
|
require 'legion/cache/pool'
|
|
7
8
|
require 'legion/cache/settings'
|
|
@@ -13,67 +14,69 @@ module Legion
|
|
|
13
14
|
extend self
|
|
14
15
|
extend Legion::Logging::Helper
|
|
15
16
|
|
|
16
|
-
def client(
|
|
17
|
-
username: nil, password: nil, logger: nil, **opts)
|
|
17
|
+
def client(**opts)
|
|
18
18
|
return @client unless @client.nil?
|
|
19
19
|
|
|
20
|
-
settings =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
replica = opts.delete(:replica) || false
|
|
26
|
-
fixed_hostname = opts.delete(:fixed_hostname)
|
|
27
|
-
db = opts.delete(:db)
|
|
28
|
-
reconnect_attempts = opts.delete(:reconnect_attempts) || [0, 0.5, 1]
|
|
29
|
-
|
|
30
|
-
@pool_size = pool_size
|
|
31
|
-
@timeout = timeout
|
|
32
|
-
@cluster_mode = Array(cluster).compact.any?
|
|
33
|
-
@component_logger = logger || log
|
|
34
|
-
|
|
35
|
-
@connection_opts = { username: username, password: password, timeout: @timeout }.compact
|
|
36
|
-
@connection_opts.merge!(redis_tls_options(port: resolve_primary_port(server: server, servers: servers, cluster: cluster)))
|
|
37
|
-
|
|
38
|
-
checkout_timeout = opts[:pool_checkout_timeout] || settings[:pool_checkout_timeout] || @timeout
|
|
39
|
-
@client = ConnectionPool.new(size: pool_size, timeout: checkout_timeout) do
|
|
40
|
-
build_redis_client(server: server, servers: servers, cluster: cluster,
|
|
41
|
-
replica: replica, fixed_hostname: fixed_hostname,
|
|
42
|
-
username: username, password: password, db: db,
|
|
43
|
-
reconnect_attempts: reconnect_attempts)
|
|
20
|
+
settings = redis_settings.merge(opts)
|
|
21
|
+
if opts.key?(:server) || opts.key?(:servers)
|
|
22
|
+
settings[:server] = opts[:server]
|
|
23
|
+
settings[:servers] = opts.fetch(:servers, [])
|
|
24
|
+
settings[:cluster] = nil unless opts.key?(:cluster)
|
|
44
25
|
end
|
|
45
|
-
@
|
|
46
|
-
|
|
26
|
+
@redis_settings = settings
|
|
27
|
+
@pool_size = settings[:pool_size] || 10
|
|
28
|
+
@timeout = settings[:timeout] || 5
|
|
29
|
+
raise ArgumentError, 'Redis timeout must be positive' unless @timeout.positive?
|
|
30
|
+
|
|
31
|
+
@cluster_mode = Array(settings[:cluster]).compact.any?
|
|
32
|
+
@component_logger = settings[:logger] || log
|
|
33
|
+
checkout_timeout = settings[:pool_checkout_timeout] || @timeout
|
|
34
|
+
pending_pool = ConnectionPool.new(size: @pool_size, timeout: checkout_timeout) do
|
|
35
|
+
build_redis_client(**settings.slice(:server, :servers, :cluster, :replica, :fixed_hostname,
|
|
36
|
+
:username, :password, :db, :reconnect_attempts))
|
|
37
|
+
end
|
|
38
|
+
pending_pool.with do |conn|
|
|
39
|
+
raise ::Redis::ConnectionError, 'Redis health check failed' unless conn.ping == 'PONG'
|
|
40
|
+
end
|
|
41
|
+
@client = pending_pool
|
|
42
|
+
cache_mark_connected(true)
|
|
47
43
|
@client
|
|
48
44
|
rescue StandardError => e
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
cache_mark_connected(false)
|
|
46
|
+
@client = nil
|
|
47
|
+
begin
|
|
48
|
+
pending_pool&.shutdown(&:close)
|
|
49
|
+
rescue StandardError => close_error
|
|
50
|
+
handle_exception(close_error, level: :warn, handled: true, operation: :redis_failed_pool_close)
|
|
51
|
+
end
|
|
52
|
+
handle_exception(e, level: :error, handled: false, operation: :redis_client)
|
|
52
53
|
raise
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
def build_redis_client(server: nil, servers: [], cluster: nil, replica: false, fixed_hostname: nil, # rubocop:disable Metrics/ParameterLists
|
|
56
57
|
username: nil, password: nil, db: nil, reconnect_attempts: [0, 0.5, 1])
|
|
57
58
|
nodes = Array(cluster).compact
|
|
59
|
+
options = { username: username, password: password, db: db, timeout: @timeout || 5,
|
|
60
|
+
reconnect_attempts: reconnect_attempts }.compact
|
|
58
61
|
if nodes.any?
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
require 'redis-clustering'
|
|
63
|
+
raise ArgumentError, 'Redis Cluster only supports database 0' unless db.nil? || db.to_i.zero?
|
|
64
|
+
|
|
65
|
+
options.merge!(redis_tls_options(port: resolve_primary_port(cluster: nodes)))
|
|
66
|
+
options[:replica] = replica
|
|
67
|
+
options[:fixed_hostname] = fixed_hostname unless fixed_hostname.nil?
|
|
68
|
+
::Redis::Cluster.new(nodes: nodes, **options)
|
|
65
69
|
else
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
::Redis.new(**redis_opts)
|
|
70
|
+
address = (Array(servers) + Array(server)).first
|
|
71
|
+
if address.to_s.start_with?('redis://', 'rediss://')
|
|
72
|
+
options[:url] = address
|
|
73
|
+
port = URI.parse(address).port || 6379
|
|
74
|
+
else
|
|
75
|
+
host, port = Legion::Cache::Settings.parse_server_address(address, default_port: 6379)
|
|
76
|
+
options.merge!(host: host, port: port)
|
|
77
|
+
end
|
|
78
|
+
options.merge!(redis_tls_options(port: port))
|
|
79
|
+
::Redis.new(**options)
|
|
77
80
|
end
|
|
78
81
|
end
|
|
79
82
|
|
|
@@ -81,32 +84,45 @@ module Legion
|
|
|
81
84
|
@cluster_mode == true
|
|
82
85
|
end
|
|
83
86
|
|
|
87
|
+
def redis_key(key)
|
|
88
|
+
namespace = redis_settings[:namespace].to_s
|
|
89
|
+
namespace.empty? ? key.to_s : "#{namespace}:#{key}"
|
|
90
|
+
end
|
|
91
|
+
|
|
84
92
|
def get(key)
|
|
85
|
-
raw = client.with { |conn| conn.get(key) }
|
|
93
|
+
raw = client.with { |conn| conn.get(redis_key(key)) }
|
|
86
94
|
result = deserialize_value(raw)
|
|
87
95
|
log.debug { "[cache] GET #{key} hit=#{!result.nil?}" }
|
|
88
96
|
result
|
|
89
97
|
rescue StandardError => e
|
|
98
|
+
cache_backend_failed(e)
|
|
90
99
|
handle_exception(e, level: :warn, handled: true, operation: :redis_get, key: key)
|
|
91
100
|
nil
|
|
92
101
|
end
|
|
93
102
|
|
|
94
103
|
def fetch(key, ttl: nil)
|
|
104
|
+
redis_ttl(ttl)
|
|
95
105
|
result = get(key)
|
|
96
106
|
return result unless result.nil? && block_given?
|
|
97
107
|
|
|
98
108
|
result = yield
|
|
99
|
-
|
|
109
|
+
begin
|
|
110
|
+
set(key, result, ttl: ttl)
|
|
111
|
+
rescue StandardError => e
|
|
112
|
+
handle_exception(e, level: :warn, handled: true, operation: :redis_fetch_store, key: key)
|
|
113
|
+
end
|
|
100
114
|
result
|
|
101
115
|
end
|
|
102
116
|
|
|
103
117
|
def set_nx(key, value, ttl: nil)
|
|
104
|
-
effective_ttl = ttl
|
|
118
|
+
effective_ttl = redis_ttl(ttl)
|
|
105
119
|
serialized = serialize_value(value)
|
|
106
|
-
|
|
120
|
+
args = effective_ttl ? { ex: effective_ttl } : {}
|
|
121
|
+
result = client.with { |conn| conn.set(redis_key(key), serialized, nx: true, **args) == 'OK' }
|
|
107
122
|
log.debug { "[cache] SET_NX #{key} ttl=#{effective_ttl.inspect} result=#{result}" }
|
|
108
123
|
result
|
|
109
124
|
rescue StandardError => e
|
|
125
|
+
cache_backend_failed(e)
|
|
110
126
|
handle_exception(e, level: :error, handled: false, operation: :redis_set_nx, key: key, ttl: effective_ttl)
|
|
111
127
|
raise
|
|
112
128
|
end
|
|
@@ -116,42 +132,77 @@ module Legion
|
|
|
116
132
|
end
|
|
117
133
|
|
|
118
134
|
def set_sync(key, value, ttl: nil, **)
|
|
119
|
-
effective_ttl = ttl
|
|
135
|
+
effective_ttl = redis_ttl(ttl)
|
|
120
136
|
args = {}
|
|
121
137
|
args[:ex] = effective_ttl unless effective_ttl.nil?
|
|
122
138
|
serialized = serialize_value(value)
|
|
123
|
-
result = client.with { |conn| conn.set(key, serialized, **args) == 'OK' }
|
|
139
|
+
result = client.with { |conn| conn.set(redis_key(key), serialized, **args) == 'OK' }
|
|
124
140
|
log.debug { "[cache] SET #{key} ttl=#{effective_ttl.inspect} success=#{result}" }
|
|
125
141
|
result
|
|
126
142
|
rescue StandardError => e
|
|
143
|
+
cache_backend_failed(e)
|
|
127
144
|
handle_exception(e, level: :error, handled: false, operation: :redis_set_sync, key: key, ttl: effective_ttl)
|
|
128
145
|
raise
|
|
129
146
|
end
|
|
130
147
|
|
|
148
|
+
def cas(key, ttl: nil)
|
|
149
|
+
effective_ttl = redis_ttl(ttl)
|
|
150
|
+
args = effective_ttl ? { ex: effective_ttl } : {}
|
|
151
|
+
qualified = redis_key(key)
|
|
152
|
+
user_error = nil
|
|
153
|
+
client.with do |conn|
|
|
154
|
+
10.times do
|
|
155
|
+
value = nil
|
|
156
|
+
committed = conn.watch(qualified) do |watched|
|
|
157
|
+
current = deserialize_value(watched.get(qualified))
|
|
158
|
+
begin
|
|
159
|
+
value = yield current
|
|
160
|
+
rescue StandardError => e
|
|
161
|
+
user_error = e
|
|
162
|
+
raise
|
|
163
|
+
end
|
|
164
|
+
serialized = serialize_value(value)
|
|
165
|
+
watched.multi { |transaction| transaction.set(qualified, serialized, **args) }
|
|
166
|
+
end
|
|
167
|
+
return value unless committed.nil?
|
|
168
|
+
end
|
|
169
|
+
raise ::Redis::CommandError, 'Redis CAS conflicted after 10 attempts'
|
|
170
|
+
end
|
|
171
|
+
rescue StandardError => e
|
|
172
|
+
raise if e.equal?(user_error)
|
|
173
|
+
|
|
174
|
+
cache_backend_failed(e)
|
|
175
|
+
handle_exception(e, level: :error, handled: false, operation: :redis_cas, key: key)
|
|
176
|
+
raise
|
|
177
|
+
end
|
|
178
|
+
|
|
131
179
|
def delete(key, **)
|
|
132
180
|
delete_sync(key)
|
|
133
181
|
end
|
|
134
182
|
|
|
135
183
|
def delete_sync(key)
|
|
136
|
-
result = client.with { |conn| conn.del(key) == 1 }
|
|
184
|
+
result = client.with { |conn| conn.del(redis_key(key)) == 1 }
|
|
137
185
|
log.debug { "[cache] DELETE #{key} success=#{result}" }
|
|
138
186
|
result
|
|
139
187
|
rescue StandardError => e
|
|
188
|
+
cache_backend_failed(e)
|
|
140
189
|
handle_exception(e, level: :error, handled: false, operation: :redis_delete_sync, key: key)
|
|
141
190
|
raise
|
|
142
191
|
end
|
|
143
192
|
|
|
144
193
|
def flush
|
|
145
194
|
result = client.with do |conn|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
195
|
+
prefix = redis_key('')
|
|
196
|
+
raise ArgumentError, 'Redis flush requires a namespace' if prefix.empty?
|
|
197
|
+
|
|
198
|
+
pattern = prefix.gsub(/[\\*?\[\]]/) { |character| "\\#{character}" }
|
|
199
|
+
conn.scan_each(match: "#{pattern}*", count: 100) { |key| conn.del(key) }
|
|
200
|
+
true
|
|
151
201
|
end
|
|
152
202
|
log.debug { '[cache] FLUSH completed' }
|
|
153
203
|
result
|
|
154
204
|
rescue StandardError => e
|
|
205
|
+
cache_backend_failed(e)
|
|
155
206
|
handle_exception(e, level: :warn, handled: true, operation: :redis_flush)
|
|
156
207
|
nil
|
|
157
208
|
end
|
|
@@ -160,18 +211,19 @@ module Legion
|
|
|
160
211
|
keys = keys.flatten
|
|
161
212
|
return {} if keys.empty?
|
|
162
213
|
|
|
163
|
-
|
|
214
|
+
qualified = keys.map { |key| redis_key(key) }
|
|
215
|
+
values = client.with do |conn|
|
|
164
216
|
if cluster_mode?
|
|
165
|
-
cluster_mget(conn,
|
|
217
|
+
cluster_mget(conn, qualified).values_at(*qualified)
|
|
166
218
|
else
|
|
167
|
-
|
|
168
|
-
keys.zip(values).to_h
|
|
219
|
+
conn.mget(*qualified)
|
|
169
220
|
end
|
|
170
221
|
end
|
|
171
|
-
result =
|
|
222
|
+
result = keys.zip(values.map { |value| deserialize_value(value) }).to_h
|
|
172
223
|
log.debug { "[cache] MGET keys=#{keys.size}" }
|
|
173
224
|
result
|
|
174
225
|
rescue StandardError => e
|
|
226
|
+
cache_backend_failed(e)
|
|
175
227
|
handle_exception(e, level: :warn, handled: true, operation: :redis_mget, key_count: keys.size)
|
|
176
228
|
{}
|
|
177
229
|
end
|
|
@@ -220,12 +272,16 @@ module Legion
|
|
|
220
272
|
raw
|
|
221
273
|
end
|
|
222
274
|
|
|
223
|
-
def
|
|
224
|
-
return
|
|
275
|
+
def redis_settings
|
|
276
|
+
return @redis_settings if @redis_settings
|
|
277
|
+
return @cache_settings if @cache_settings
|
|
278
|
+
|
|
279
|
+
shared = defined?(Legion::Settings) ? Legion::Settings[:cache] : nil
|
|
280
|
+
Legion::Cache::Settings.default.merge(shared || {})
|
|
281
|
+
end
|
|
225
282
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
3600
|
|
283
|
+
def default_ttl
|
|
284
|
+
redis_settings[:default_ttl]
|
|
229
285
|
end
|
|
230
286
|
|
|
231
287
|
def cluster_mget(conn, keys)
|
|
@@ -238,61 +294,34 @@ module Legion
|
|
|
238
294
|
result
|
|
239
295
|
end
|
|
240
296
|
|
|
241
|
-
def cluster_mset(conn, hash)
|
|
242
|
-
groups = group_keys_by_slot(hash.keys)
|
|
243
|
-
groups.each_value do |group_keys|
|
|
244
|
-
pairs = group_keys.flat_map { |k| [k, hash[k]] }
|
|
245
|
-
conn.mset(*pairs)
|
|
246
|
-
end
|
|
247
|
-
true
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
def cluster_flush(conn)
|
|
251
|
-
node_info = conn.cluster('nodes')
|
|
252
|
-
primaries = node_info.lines.select { |l| l.include?('master') }.map { |l| l.split[1].split('@').first }
|
|
253
|
-
primaries.each do |addr|
|
|
254
|
-
host, port = Legion::Cache::Settings.parse_server_address(addr, default_port: 6379)
|
|
255
|
-
node = ::Redis.new(host: host, port: port.to_i, **(@connection_opts || {}))
|
|
256
|
-
node.flushdb
|
|
257
|
-
node.close
|
|
258
|
-
end
|
|
259
|
-
true
|
|
260
|
-
rescue StandardError => e
|
|
261
|
-
handle_exception(e, level: :warn, handled: true, operation: :cluster_flush, fallback: :single_flushdb)
|
|
262
|
-
conn.flushdb == 'OK'
|
|
263
|
-
end
|
|
264
|
-
|
|
265
297
|
def group_keys_by_slot(keys)
|
|
266
|
-
|
|
267
|
-
keys.group_by { |k| ::Redis::Cluster::KeySlotConverter.convert(k) }
|
|
268
|
-
else
|
|
269
|
-
{ 0 => keys }
|
|
270
|
-
end
|
|
298
|
+
keys.group_by { |key| ::RedisClient::Cluster::KeySlotConverter.convert(key) }
|
|
271
299
|
end
|
|
272
300
|
|
|
273
|
-
def
|
|
274
|
-
|
|
275
|
-
return
|
|
301
|
+
def redis_ttl(ttl)
|
|
302
|
+
value = ttl.nil? ? default_ttl : ttl
|
|
303
|
+
return nil if value.nil?
|
|
276
304
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
'unknown'
|
|
305
|
+
raise ArgumentError, 'TTL must not be negative' if value.negative?
|
|
306
|
+
|
|
307
|
+
value.zero? ? nil : value
|
|
281
308
|
end
|
|
282
309
|
|
|
283
310
|
def resolve_primary_port(server: nil, servers: [], cluster: nil)
|
|
284
|
-
|
|
285
|
-
return 6379 if
|
|
311
|
+
address = Array(cluster).compact.first || (Array(servers) + Array(server)).first
|
|
312
|
+
return address.fetch(:port, 6379).to_i if address.is_a?(Hash)
|
|
313
|
+
return URI.parse(address).port || 6379 if address.to_s.start_with?('redis://', 'rediss://')
|
|
286
314
|
|
|
287
|
-
|
|
288
|
-
_, port = Legion::Cache::Settings.parse_server_address(resolved.first, default_port: 6379)
|
|
289
|
-
port.to_i
|
|
290
|
-
rescue StandardError
|
|
291
|
-
6379
|
|
315
|
+
Legion::Cache::Settings.parse_server_address(address, default_port: 6379).last
|
|
292
316
|
end
|
|
293
317
|
|
|
294
318
|
def redis_tls_options(port:)
|
|
295
|
-
|
|
319
|
+
unless defined?(Legion::Crypt::TLS)
|
|
320
|
+
settings = cache_tls_settings
|
|
321
|
+
return {} if settings.empty? || settings[:enabled] == false
|
|
322
|
+
|
|
323
|
+
raise ArgumentError, 'Legion::Crypt::TLS is required for configured Redis TLS'
|
|
324
|
+
end
|
|
296
325
|
|
|
297
326
|
tls = Legion::Crypt::TLS.resolve(cache_tls_settings, port: port)
|
|
298
327
|
return {} unless tls[:enabled]
|
|
@@ -301,17 +330,14 @@ module Legion
|
|
|
301
330
|
verify_mode: tls[:verify] == :none ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
|
|
302
331
|
}
|
|
303
332
|
ssl_params[:ca_file] = tls[:ca] if tls[:ca]
|
|
333
|
+
ssl_params[:cert] = OpenSSL::X509::Certificate.new(File.read(tls[:cert])) if tls[:cert]
|
|
334
|
+
ssl_params[:key] = OpenSSL::PKey.read(File.read(tls[:key])) if tls[:key]
|
|
304
335
|
|
|
305
336
|
{ ssl: true, ssl_params: ssl_params }
|
|
306
337
|
end
|
|
307
338
|
|
|
308
339
|
def cache_tls_settings
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
Legion::Settings[:cache][:tls] || {}
|
|
312
|
-
rescue StandardError => e
|
|
313
|
-
handle_exception(e, level: :warn, handled: true, operation: :cache_tls_settings)
|
|
314
|
-
{}
|
|
340
|
+
redis_settings[:tls] || {}
|
|
315
341
|
end
|
|
316
342
|
end
|
|
317
343
|
end
|
|
@@ -28,7 +28,7 @@ module Legion
|
|
|
28
28
|
|
|
29
29
|
Legion::Cache.pool.with do |conn|
|
|
30
30
|
flat = hash.flat_map { |k, v| [k.to_s, v.to_s] }
|
|
31
|
-
conn.hset(key, *flat)
|
|
31
|
+
conn.hset(Legion::Cache.redis_key(key), *flat)
|
|
32
32
|
end
|
|
33
33
|
log.debug "[cache:redis_hash] HSET #{key} fields=#{hash.size}"
|
|
34
34
|
true
|
|
@@ -42,7 +42,7 @@ module Legion
|
|
|
42
42
|
return nil unless redis_available?
|
|
43
43
|
|
|
44
44
|
result = Legion::Cache.pool.with do |conn|
|
|
45
|
-
conn.hgetall(key)
|
|
45
|
+
conn.hgetall(Legion::Cache.redis_key(key))
|
|
46
46
|
end
|
|
47
47
|
log.debug "[cache:redis_hash] HGETALL #{key} fields=#{result.size}"
|
|
48
48
|
result
|
|
@@ -56,7 +56,7 @@ module Legion
|
|
|
56
56
|
return 0 unless redis_available?
|
|
57
57
|
|
|
58
58
|
result = Legion::Cache.pool.with do |conn|
|
|
59
|
-
conn.hdel(key, *fields)
|
|
59
|
+
conn.hdel(Legion::Cache.redis_key(key), *fields)
|
|
60
60
|
end
|
|
61
61
|
log.debug "[cache:redis_hash] HDEL #{key} fields=#{fields.size} removed=#{result}"
|
|
62
62
|
result
|
|
@@ -70,7 +70,7 @@ module Legion
|
|
|
70
70
|
return false unless redis_available?
|
|
71
71
|
|
|
72
72
|
Legion::Cache.pool.with do |conn|
|
|
73
|
-
conn.zadd(key, score.to_f, member.to_s)
|
|
73
|
+
conn.zadd(Legion::Cache.redis_key(key), score.to_f, member.to_s)
|
|
74
74
|
end
|
|
75
75
|
log.debug "[cache:redis_hash] ZADD #{key} member=#{member}"
|
|
76
76
|
true
|
|
@@ -88,7 +88,7 @@ module Legion
|
|
|
88
88
|
opts[:limit] = limit if limit
|
|
89
89
|
|
|
90
90
|
result = Legion::Cache.pool.with do |conn|
|
|
91
|
-
conn.zrangebyscore(key, min, max, **opts)
|
|
91
|
+
conn.zrangebyscore(Legion::Cache.redis_key(key), min, max, **opts)
|
|
92
92
|
end
|
|
93
93
|
log.debug "[cache:redis_hash] ZRANGEBYSCORE #{key} results=#{result.size}"
|
|
94
94
|
result
|
|
@@ -102,7 +102,7 @@ module Legion
|
|
|
102
102
|
return false unless redis_available?
|
|
103
103
|
|
|
104
104
|
Legion::Cache.pool.with do |conn|
|
|
105
|
-
conn.zrem(key, member.to_s)
|
|
105
|
+
conn.zrem(Legion::Cache.redis_key(key), member.to_s)
|
|
106
106
|
end
|
|
107
107
|
log.debug "[cache:redis_hash] ZREM #{key} member=#{member}"
|
|
108
108
|
true
|
|
@@ -113,19 +113,22 @@ module Legion
|
|
|
113
113
|
|
|
114
114
|
# Set a TTL (in seconds) on a key.
|
|
115
115
|
def expire(key, seconds)
|
|
116
|
+
raise ArgumentError, 'TTL must not be negative' if seconds.negative?
|
|
116
117
|
return false unless redis_available?
|
|
117
118
|
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
begin
|
|
120
|
+
Legion::Cache.pool.with do |conn|
|
|
121
|
+
qualified = Legion::Cache.redis_key(key)
|
|
122
|
+
seconds.zero? ? conn.persist(qualified) : conn.expire(qualified, seconds)
|
|
123
|
+
end
|
|
124
|
+
rescue StandardError => e
|
|
125
|
+
log_redis_error('expire', e)
|
|
126
|
+
false
|
|
120
127
|
end
|
|
121
|
-
log.debug "[cache:redis_hash] EXPIRE #{key} seconds=#{seconds} success=#{result}"
|
|
122
|
-
result
|
|
123
|
-
rescue StandardError => e
|
|
124
|
-
log_redis_error('expire', e)
|
|
125
|
-
false
|
|
126
128
|
end
|
|
127
129
|
|
|
128
130
|
def log_redis_error(method, error)
|
|
131
|
+
Legion::Cache.send(:cache_backend_failed, error)
|
|
129
132
|
handle_exception(error, level: :warn, handled: true, operation: method)
|
|
130
133
|
end
|
|
131
134
|
end
|
|
@@ -81,6 +81,14 @@ module Legion
|
|
|
81
81
|
|
|
82
82
|
DEFAULT_PORTS = { 'dalli' => 11_211, 'redis' => 6379 }.freeze
|
|
83
83
|
|
|
84
|
+
def self.connection_options(settings, overrides)
|
|
85
|
+
resolved = settings.merge(overrides)
|
|
86
|
+
resolved[:servers] = [] if overrides.key?(:server) && !overrides.key?(:servers)
|
|
87
|
+
resolved[:server] = nil if overrides.key?(:servers) && !overrides.key?(:server)
|
|
88
|
+
resolved[:cluster] = nil if (overrides.key?(:server) || overrides.key?(:servers)) && !overrides.key?(:cluster)
|
|
89
|
+
resolved
|
|
90
|
+
end
|
|
91
|
+
|
|
84
92
|
def self.resolve_servers(driver:, server: nil, servers: [], port: nil)
|
|
85
93
|
gem_driver = normalize_driver(driver)
|
|
86
94
|
port ||= DEFAULT_PORTS.fetch(gem_driver, 11_211)
|