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.rb
CHANGED
|
@@ -4,7 +4,6 @@ require 'legion/logging/helper'
|
|
|
4
4
|
require 'legion/cache/version'
|
|
5
5
|
require 'legion/cache/settings'
|
|
6
6
|
require 'legion/cache/cacheable'
|
|
7
|
-
|
|
8
7
|
require 'legion/cache/memcached'
|
|
9
8
|
require 'legion/cache/redis'
|
|
10
9
|
require 'legion/cache/redis_hash'
|
|
@@ -26,18 +25,17 @@ module Legion
|
|
|
26
25
|
|
|
27
26
|
class << self
|
|
28
27
|
include Legion::Logging::Helper
|
|
28
|
+
include Legion::Cache::Pool
|
|
29
29
|
|
|
30
30
|
def enabled?
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Legion::Settings.dig(:cache, :enabled) != false
|
|
34
|
-
rescue StandardError => e
|
|
35
|
-
handle_exception(e, level: :warn, handled: true, operation: :cache_enabled)
|
|
36
|
-
true
|
|
31
|
+
!defined?(Legion::Settings) || Legion::Settings.dig(:cache, :enabled) != false
|
|
37
32
|
end
|
|
38
33
|
|
|
39
34
|
def connected?
|
|
40
|
-
|
|
35
|
+
return Legion::Cache::Memory.connected? if using_memory?
|
|
36
|
+
return Legion::Cache::Local.enabled? && Legion::Cache::Local.connected? if using_local?
|
|
37
|
+
|
|
38
|
+
enabled? && @connected.value
|
|
41
39
|
end
|
|
42
40
|
|
|
43
41
|
def driver_name
|
|
@@ -49,65 +47,50 @@ module Legion
|
|
|
49
47
|
|
|
50
48
|
def stats
|
|
51
49
|
{
|
|
52
|
-
driver:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
using_memory: using_memory?,
|
|
58
|
-
pool_size: safe_pool_size,
|
|
59
|
-
pool_available: safe_pool_available,
|
|
60
|
-
async_pool_size: async_writer_pool_size,
|
|
61
|
-
async_queue_depth: async_writer_queue_depth,
|
|
62
|
-
async_processed: async_writer_processed_count,
|
|
63
|
-
async_failed: async_writer_failed_count,
|
|
64
|
-
reconnect_attempts: reconnector_attempts,
|
|
65
|
-
uptime: uptime_seconds
|
|
50
|
+
driver: driver_name, servers: resolved_servers, enabled: enabled?, connected: connected?,
|
|
51
|
+
using_local: using_local?, using_memory: using_memory?, pool_size: pool_size, pool_available: available,
|
|
52
|
+
async_pool_size: async_writer.pool_size, async_queue_depth: async_writer.queue_depth,
|
|
53
|
+
async_processed: async_writer.processed_count, async_failed: async_writer.failed_count,
|
|
54
|
+
reconnect_attempts: @reconnector&.attempts || 0, uptime: @setup_at ? (Time.now - @setup_at).to_i : 0
|
|
66
55
|
}.freeze
|
|
67
|
-
rescue StandardError => e
|
|
68
|
-
handle_exception(e, level: :warn, handled: true, operation: :cache_stats)
|
|
69
|
-
{ error: e.message }.freeze
|
|
70
56
|
end
|
|
71
57
|
|
|
72
|
-
def setup(**)
|
|
73
|
-
return
|
|
74
|
-
return Legion::Settings[:cache][:connected] = true if connected?
|
|
58
|
+
def setup(**opts)
|
|
59
|
+
return true if connected?
|
|
75
60
|
|
|
61
|
+
@initialized = true
|
|
62
|
+
@connection_options = Legion::Cache::Settings.connection_options(@connection_options || {}, opts)
|
|
76
63
|
@setup_at = Time.now
|
|
77
|
-
|
|
78
|
-
async_writer.start
|
|
79
|
-
|
|
80
64
|
if ENV['LEGION_MODE'] == 'lite'
|
|
65
|
+
async_writer.start
|
|
81
66
|
Legion::Cache::Memory.setup
|
|
82
67
|
@using_memory.make_true
|
|
83
|
-
@
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return
|
|
68
|
+
@using_local.make_false
|
|
69
|
+
cache_mark_connected(true)
|
|
70
|
+
return true
|
|
87
71
|
end
|
|
88
72
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
73
|
+
Legion::Cache::Local.setup
|
|
74
|
+
unless enabled?
|
|
75
|
+
result = select_local_fallback
|
|
76
|
+
async_writer.start if result
|
|
77
|
+
return result
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
async_writer.start
|
|
81
|
+
setup_shared
|
|
92
82
|
end
|
|
93
83
|
|
|
94
84
|
def shutdown
|
|
95
|
-
log.info 'Shutting down Legion::Cache'
|
|
96
|
-
# 1. Drain async writer FIRST (while pool is still alive)
|
|
97
85
|
async_writer.stop(timeout: configured_shutdown_timeout)
|
|
98
|
-
# 2. Stop reconnector
|
|
99
86
|
stop_reconnector
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
else
|
|
104
|
-
close unless using_local?
|
|
105
|
-
Legion::Cache::Local.shutdown if Legion::Cache::Local.connected?
|
|
106
|
-
end
|
|
87
|
+
close_existing_shared_client
|
|
88
|
+
Legion::Cache::Local.shutdown
|
|
89
|
+
Legion::Cache::Memory.shutdown if using_memory?
|
|
107
90
|
@using_local.make_false
|
|
108
91
|
@using_memory.make_false
|
|
109
|
-
|
|
110
|
-
|
|
92
|
+
cache_mark_connected(false)
|
|
93
|
+
@initialized = false
|
|
111
94
|
end
|
|
112
95
|
|
|
113
96
|
def local
|
|
@@ -119,468 +102,349 @@ module Legion
|
|
|
119
102
|
end
|
|
120
103
|
|
|
121
104
|
def using_local?
|
|
122
|
-
@using_local
|
|
105
|
+
@using_local.value
|
|
123
106
|
end
|
|
124
107
|
|
|
125
108
|
def using_memory?
|
|
126
|
-
@using_memory
|
|
109
|
+
@using_memory.value
|
|
127
110
|
end
|
|
128
111
|
|
|
129
|
-
def client(**
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
@connected.make_true
|
|
135
|
-
@active_shared_driver = nil
|
|
136
|
-
Legion::Settings[:cache][:connected] = true if defined?(Legion::Settings)
|
|
137
|
-
return Legion::Cache::Memory.client
|
|
138
|
-
end
|
|
112
|
+
def client(**)
|
|
113
|
+
setup(**) unless @initialized
|
|
114
|
+
return Legion::Cache::Memory.client if using_memory?
|
|
115
|
+
return Legion::Cache::Local.client if using_local?
|
|
116
|
+
return nil unless connected?
|
|
139
117
|
|
|
140
|
-
|
|
141
|
-
@using_memory.make_false
|
|
142
|
-
@using_local.make_false
|
|
143
|
-
result = super
|
|
144
|
-
# super (Pool) sets @connected to a plain boolean; restore AtomicBoolean
|
|
145
|
-
@connected = Concurrent::AtomicBoolean.new(true)
|
|
146
|
-
Legion::Settings[:cache][:connected] = true if defined?(Legion::Settings)
|
|
147
|
-
result
|
|
148
|
-
rescue StandardError
|
|
149
|
-
@connected = Concurrent::AtomicBoolean.new(false)
|
|
150
|
-
Legion::Settings[:cache][:connected] = false if defined?(Legion::Settings)
|
|
151
|
-
raise
|
|
118
|
+
@client
|
|
152
119
|
end
|
|
153
120
|
|
|
154
121
|
def get(key)
|
|
155
|
-
|
|
156
|
-
return
|
|
157
|
-
return Legion::Cache::Local.get(key) if failback_to_local?
|
|
122
|
+
result = cache_operation(:get, key, failure: nil)
|
|
123
|
+
return result unless result.nil?
|
|
158
124
|
|
|
159
|
-
|
|
160
|
-
super
|
|
161
|
-
rescue StandardError => e
|
|
162
|
-
handle_exception(e, level: :warn, handled: true, operation: :cache_get, key: key)
|
|
163
|
-
nil
|
|
125
|
+
local_cache_get(key)
|
|
164
126
|
end
|
|
165
127
|
|
|
166
128
|
def phi_max_ttl
|
|
167
|
-
|
|
129
|
+
configured = defined?(Legion::Settings) ? Legion::Settings.dig(:cache, :compliance, :phi_max_ttl) : nil
|
|
130
|
+
return 3600 if configured.nil?
|
|
131
|
+
return configured if configured.is_a?(Numeric) && configured.finite? && configured.positive?
|
|
168
132
|
|
|
169
|
-
|
|
170
|
-
rescue StandardError => e
|
|
171
|
-
handle_exception(e, level: :warn, handled: true, operation: :cache_phi_max_ttl)
|
|
172
|
-
3600
|
|
133
|
+
raise ArgumentError, 'phi_max_ttl must be positive and finite'
|
|
173
134
|
end
|
|
174
135
|
|
|
175
136
|
def enforce_phi_ttl(ttl, phi: false, **)
|
|
176
137
|
return ttl unless phi == true
|
|
138
|
+
raise ArgumentError, 'PHI ttl must be numeric and finite' unless ttl.nil? || (ttl.is_a?(Numeric) && ttl.finite?)
|
|
139
|
+
return phi_max_ttl if ttl.nil? || !ttl.positive?
|
|
177
140
|
|
|
178
|
-
|
|
179
|
-
[ttl, max].min
|
|
141
|
+
[ttl, phi_max_ttl].min
|
|
180
142
|
end
|
|
181
143
|
|
|
182
144
|
def set(key, value, ttl: nil, async: true, phi: false)
|
|
183
|
-
|
|
145
|
+
setup unless @initialized || connected?
|
|
146
|
+
return false unless connected?
|
|
184
147
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
148
|
+
effective_ttl = resolve_ttl(ttl, phi: phi)
|
|
149
|
+
if async
|
|
150
|
+
async_writer.enqueue(key: key) { set_sync(key, value, ttl: effective_ttl) }
|
|
188
151
|
else
|
|
189
|
-
|
|
152
|
+
set_sync(key, value, ttl: effective_ttl)
|
|
190
153
|
end
|
|
191
154
|
end
|
|
192
155
|
|
|
193
|
-
def set_nx(key, value, ttl: nil)
|
|
194
|
-
|
|
195
|
-
return Legion::Cache::Memory.set_nx(key, value, ttl: effective_ttl) if using_memory?
|
|
196
|
-
return Legion::Cache::Local.set_nx(key, value, ttl: effective_ttl) if using_local?
|
|
197
|
-
return Legion::Cache::Local.set_nx(key, value, ttl: effective_ttl) if failback_to_local?
|
|
198
|
-
|
|
199
|
-
configure_shared_adapter!
|
|
200
|
-
super
|
|
201
|
-
rescue StandardError => e
|
|
202
|
-
handle_exception(e, level: :warn, handled: true, operation: :cache_set_nx, key: key)
|
|
203
|
-
false
|
|
156
|
+
def set_nx(key, value, ttl: nil, phi: false)
|
|
157
|
+
cache_operation(:set_nx, key, value, ttl: resolve_ttl(ttl, phi: phi), failure: false)
|
|
204
158
|
end
|
|
205
159
|
|
|
206
|
-
def set_sync(key, value, ttl: nil, **)
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
return Legion::Cache::Local.set_sync(key, value, ttl: ttl) if failback_to_local?
|
|
160
|
+
def set_sync(key, value, ttl: nil, phi: false, **)
|
|
161
|
+
cache_operation(:set_sync, key, value, ttl: resolve_ttl(ttl, phi: phi), failure: false)
|
|
162
|
+
end
|
|
210
163
|
|
|
211
|
-
|
|
212
|
-
|
|
164
|
+
def cas(key, ttl: nil, phi: false, &)
|
|
165
|
+
cache_operation(:cas, key, ttl: resolve_ttl(ttl, phi: phi), failure: false, &)
|
|
213
166
|
end
|
|
214
167
|
|
|
215
|
-
def fetch(key, ttl: nil
|
|
216
|
-
|
|
217
|
-
return
|
|
218
|
-
return
|
|
168
|
+
def fetch(key, ttl: nil)
|
|
169
|
+
cached = get(key)
|
|
170
|
+
return cached unless cached.nil?
|
|
171
|
+
return nil unless block_given?
|
|
219
172
|
|
|
220
|
-
|
|
221
|
-
|
|
173
|
+
value = yield
|
|
174
|
+
set_sync(key, value, ttl: ttl)
|
|
175
|
+
value
|
|
222
176
|
end
|
|
223
177
|
|
|
224
178
|
def delete(key, async: true)
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
179
|
+
setup unless @initialized || connected?
|
|
180
|
+
return false unless connected?
|
|
181
|
+
|
|
182
|
+
if async
|
|
183
|
+
async_writer.enqueue(key: key) { delete_sync(key) }
|
|
228
184
|
else
|
|
229
|
-
|
|
185
|
+
delete_sync(key)
|
|
230
186
|
end
|
|
231
187
|
end
|
|
232
188
|
|
|
233
189
|
def delete_sync(key)
|
|
234
|
-
|
|
235
|
-
return Legion::Cache::Local.delete_sync(key) if using_local?
|
|
236
|
-
return Legion::Cache::Local.delete_sync(key) if failback_to_local?
|
|
237
|
-
|
|
238
|
-
configure_shared_adapter!
|
|
239
|
-
super
|
|
190
|
+
cache_operation(:delete_sync, key, failure: false)
|
|
240
191
|
end
|
|
241
192
|
|
|
242
193
|
def flush
|
|
243
|
-
|
|
244
|
-
return Legion::Cache::Local.flush if using_local?
|
|
245
|
-
return Legion::Cache::Local.flush if failback_to_local?
|
|
246
|
-
|
|
247
|
-
configure_shared_adapter!
|
|
248
|
-
super
|
|
194
|
+
cache_operation(:flush, failure: false)
|
|
249
195
|
end
|
|
250
196
|
|
|
251
197
|
def mget(*keys)
|
|
252
198
|
keys = keys.flatten
|
|
253
199
|
return {} if keys.empty?
|
|
254
|
-
return keys.to_h { |key| [key, Legion::Cache::Memory.get(key)] } if using_memory?
|
|
255
|
-
return Legion::Cache::Local.mget(*keys) if using_local?
|
|
256
|
-
return Legion::Cache::Local.mget(*keys) if failback_to_local?
|
|
257
200
|
|
|
258
|
-
|
|
259
|
-
|
|
201
|
+
result = cache_operation(:mget, *keys, failure: {})
|
|
202
|
+
return result if using_memory? || using_local?
|
|
203
|
+
|
|
204
|
+
local_cache_mget(result, keys)
|
|
260
205
|
end
|
|
261
206
|
|
|
262
|
-
def mset(hash, ttl: nil, async: true)
|
|
263
|
-
return
|
|
207
|
+
def mset(hash, ttl: nil, async: true, phi: false)
|
|
208
|
+
return mset_sync(hash, ttl: ttl, phi: phi) unless async
|
|
264
209
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
210
|
+
setup unless @initialized || connected?
|
|
211
|
+
return false unless connected?
|
|
212
|
+
|
|
213
|
+
effective_ttl = resolve_ttl(ttl, phi: phi)
|
|
214
|
+
accepted = true
|
|
215
|
+
hash.each do |key, value|
|
|
216
|
+
result = async_writer.enqueue(key: key) { set_sync(key, value, ttl: effective_ttl) }
|
|
217
|
+
accepted = false unless result
|
|
270
218
|
end
|
|
219
|
+
accepted
|
|
271
220
|
end
|
|
272
221
|
|
|
273
|
-
def mset_sync(hash, ttl: nil, **)
|
|
222
|
+
def mset_sync(hash, ttl: nil, phi: false, **)
|
|
274
223
|
return true if hash.empty?
|
|
275
|
-
return hash.each { |key, value| Legion::Cache::Memory.set_sync(key, value, ttl: ttl) } && true if using_memory?
|
|
276
|
-
return Legion::Cache::Local.mset(hash, ttl: ttl) if using_local?
|
|
277
|
-
return Legion::Cache::Local.mset(hash, ttl: ttl) if failback_to_local?
|
|
278
224
|
|
|
279
|
-
|
|
280
|
-
super
|
|
225
|
+
cache_operation(:mset_sync, hash, ttl: resolve_ttl(ttl, phi: phi), failure: false)
|
|
281
226
|
end
|
|
282
227
|
|
|
283
228
|
def close
|
|
284
|
-
|
|
285
|
-
Legion::Cache::Memory.shutdown
|
|
286
|
-
@using_memory.make_false
|
|
287
|
-
@connected.make_false
|
|
288
|
-
Legion::Settings[:cache][:connected] = false if defined?(Legion::Settings)
|
|
289
|
-
return false
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
if using_local?
|
|
293
|
-
Legion::Cache::Local.close
|
|
294
|
-
@using_local.make_false
|
|
295
|
-
@connected.make_false
|
|
296
|
-
Legion::Settings[:cache][:connected] = false if defined?(Legion::Settings)
|
|
297
|
-
return false
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
return false unless instance_variable_defined?(:@client) && @client
|
|
301
|
-
|
|
302
|
-
configure_shared_adapter!
|
|
303
|
-
result = super
|
|
304
|
-
@connected = Concurrent::AtomicBoolean.new(false)
|
|
305
|
-
Legion::Settings[:cache][:connected] = false if defined?(Legion::Settings)
|
|
306
|
-
result
|
|
229
|
+
shutdown
|
|
307
230
|
end
|
|
308
231
|
|
|
309
|
-
def restart(**
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
@using_local.make_false
|
|
313
|
-
result = super
|
|
314
|
-
@connected = Concurrent::AtomicBoolean.new(true)
|
|
315
|
-
Legion::Settings[:cache][:connected] = true if defined?(Legion::Settings)
|
|
316
|
-
result
|
|
232
|
+
def restart(**)
|
|
233
|
+
shutdown
|
|
234
|
+
setup(**)
|
|
317
235
|
end
|
|
318
236
|
|
|
319
237
|
def size
|
|
320
|
-
|
|
321
|
-
return Legion::Cache::Local.size if using_local?
|
|
322
|
-
|
|
323
|
-
configure_shared_adapter!
|
|
324
|
-
super
|
|
238
|
+
pool_size
|
|
325
239
|
end
|
|
326
240
|
|
|
327
241
|
def available
|
|
242
|
+
return 0 unless connected?
|
|
328
243
|
return Legion::Cache::Memory.available if using_memory?
|
|
329
244
|
return Legion::Cache::Local.available if using_local?
|
|
330
245
|
|
|
331
|
-
|
|
332
|
-
super
|
|
246
|
+
@client&.available || 0
|
|
333
247
|
end
|
|
334
248
|
|
|
335
249
|
def pool_size
|
|
250
|
+
return 0 unless connected?
|
|
336
251
|
return Legion::Cache::Memory.size if using_memory?
|
|
337
252
|
return Legion::Cache::Local.pool_size if using_local?
|
|
338
253
|
|
|
339
|
-
|
|
340
|
-
super
|
|
254
|
+
@client&.size || 0
|
|
341
255
|
end
|
|
342
256
|
|
|
343
257
|
def timeout
|
|
258
|
+
return 0 unless connected?
|
|
344
259
|
return 0 if using_memory?
|
|
345
260
|
return Legion::Cache::Local.timeout if using_local?
|
|
346
261
|
|
|
347
|
-
|
|
348
|
-
super
|
|
262
|
+
@timeout || shared_settings[:timeout] || 5
|
|
349
263
|
end
|
|
350
264
|
|
|
351
265
|
private
|
|
352
266
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
267
|
+
attr_reader :async_writer
|
|
268
|
+
|
|
269
|
+
def cache_operation(operation, *, failure:, **, &block)
|
|
270
|
+
setup unless @initialized || connected?
|
|
271
|
+
return failure unless connected?
|
|
356
272
|
|
|
357
|
-
|
|
358
|
-
return Legion::Cache::
|
|
359
|
-
return Legion::Cache::Local.set(key, value, ttl: ttl) if using_local?
|
|
360
|
-
return Legion::Cache::Local.set(key, value, ttl: ttl) if failback_to_local?
|
|
273
|
+
return Legion::Cache::Memory.public_send(operation, *, **, &block) if using_memory?
|
|
274
|
+
return Legion::Cache::Local.public_send(operation, *, **, &block) if using_local?
|
|
361
275
|
|
|
362
276
|
configure_shared_adapter!
|
|
363
|
-
|
|
364
|
-
|
|
277
|
+
user_error = nil
|
|
278
|
+
guarded = if block
|
|
279
|
+
proc do |value|
|
|
280
|
+
block.call(value)
|
|
281
|
+
rescue StandardError => e
|
|
282
|
+
user_error = e
|
|
283
|
+
raise
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
begin
|
|
287
|
+
result = @shared_adapter.instance_method(operation).bind_call(self, *, **, &guarded)
|
|
288
|
+
rescue StandardError => e
|
|
289
|
+
raise if e.equal?(user_error) || !cache_connection_error?(e)
|
|
290
|
+
|
|
291
|
+
cache_backend_failed(e)
|
|
292
|
+
handle_exception(e, level: :warn, handled: true, operation: operation)
|
|
293
|
+
result = failure
|
|
294
|
+
end
|
|
295
|
+
return Legion::Cache::Local.public_send(operation, *, **, &block) if using_local? && connected?
|
|
365
296
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
return Legion::Cache::Local.delete(key) if using_local?
|
|
369
|
-
return Legion::Cache::Local.delete(key) if failback_to_local?
|
|
297
|
+
connected? ? result : failure
|
|
298
|
+
end
|
|
370
299
|
|
|
371
|
-
|
|
372
|
-
|
|
300
|
+
def cache_mark_connected(value)
|
|
301
|
+
@connected.value = value
|
|
302
|
+
Legion::Settings[:cache][:connected] = value if defined?(Legion::Settings)
|
|
373
303
|
end
|
|
374
304
|
|
|
375
|
-
def
|
|
376
|
-
return
|
|
377
|
-
return Legion::Cache::Local.mset(hash, ttl: ttl) if using_local?
|
|
378
|
-
return Legion::Cache::Local.mset(hash, ttl: ttl) if failback_to_local?
|
|
305
|
+
def cache_backend_failed(error)
|
|
306
|
+
return unless cache_connection_error?(error)
|
|
379
307
|
|
|
380
|
-
|
|
381
|
-
|
|
308
|
+
cache_mark_connected(false)
|
|
309
|
+
select_local_fallback
|
|
310
|
+
start_reconnector
|
|
382
311
|
end
|
|
383
312
|
|
|
384
|
-
def
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
else
|
|
390
|
-
true
|
|
391
|
-
end
|
|
392
|
-
setting && (!enabled? || !connected?)
|
|
393
|
-
rescue StandardError => e
|
|
394
|
-
handle_exception(e, level: :warn, handled: true, operation: :cache_failback_check)
|
|
395
|
-
false
|
|
313
|
+
def select_local_fallback
|
|
314
|
+
available = local_fallback_available?
|
|
315
|
+
@using_local.value = available
|
|
316
|
+
cache_mark_connected(available)
|
|
317
|
+
available
|
|
396
318
|
end
|
|
397
319
|
|
|
398
320
|
def resolve_ttl(ttl, phi: false)
|
|
399
|
-
|
|
400
|
-
enforce_phi_ttl(effective, phi: phi)
|
|
321
|
+
enforce_phi_ttl(ttl || shared_settings[:default_ttl] || 3600, phi: phi)
|
|
401
322
|
end
|
|
402
323
|
|
|
403
|
-
def
|
|
404
|
-
return
|
|
324
|
+
def local_cache_get(key)
|
|
325
|
+
return nil unless local_fallback_available?
|
|
405
326
|
|
|
406
|
-
Legion::
|
|
407
|
-
rescue StandardError
|
|
408
|
-
3600
|
|
327
|
+
Legion::Cache::Local.get(key)
|
|
409
328
|
end
|
|
410
329
|
|
|
411
|
-
def
|
|
412
|
-
return
|
|
330
|
+
def local_cache_mget(shared_values, keys)
|
|
331
|
+
return shared_values unless local_fallback_available?
|
|
413
332
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
333
|
+
missing = keys.select { |key| shared_values[key].nil? }
|
|
334
|
+
return shared_values if missing.empty?
|
|
335
|
+
|
|
336
|
+
Legion::Cache::Local.mget(*missing).each do |key, value|
|
|
337
|
+
shared_values[key] = value unless value.nil?
|
|
338
|
+
end
|
|
339
|
+
shared_values
|
|
417
340
|
end
|
|
418
341
|
|
|
419
|
-
def
|
|
420
|
-
|
|
421
|
-
|
|
342
|
+
def local_fallback_available?
|
|
343
|
+
shared_settings[:failback_to_local] != false &&
|
|
344
|
+
Legion::Cache::Local.enabled? &&
|
|
345
|
+
Legion::Cache::Local.connected?
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def setup_shared
|
|
349
|
+
configure_shared_adapter!
|
|
350
|
+
settings = Legion::Cache::Settings.connection_options(shared_settings, @connection_options || {})
|
|
351
|
+
@shared_adapter.instance_method(:client).bind_call(self, **settings, logger: log)
|
|
422
352
|
@using_local.make_false
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
log.info "Legion::Cache connected (driver=#{driver}) to #{servers}"
|
|
353
|
+
@using_memory.make_false
|
|
354
|
+
cache_mark_connected(true)
|
|
355
|
+
true
|
|
427
356
|
rescue StandardError => e
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
@connected.make_false
|
|
436
|
-
Legion::Settings[:cache][:connected] = false
|
|
437
|
-
log.error 'Legion::Cache shared and local adapters are unavailable'
|
|
438
|
-
end
|
|
439
|
-
start_reconnector if enabled?
|
|
357
|
+
close_existing_shared_client
|
|
358
|
+
raise unless cache_connection_error?(e)
|
|
359
|
+
|
|
360
|
+
handle_exception(e, level: :warn, handled: true, operation: :setup_shared)
|
|
361
|
+
result = select_local_fallback
|
|
362
|
+
start_reconnector
|
|
363
|
+
result
|
|
440
364
|
end
|
|
441
365
|
|
|
442
366
|
def reconnect_shared!
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
367
|
+
return false unless reconnect_enabled?
|
|
368
|
+
|
|
369
|
+
close_existing_shared_client
|
|
370
|
+
configure_shared_adapter!
|
|
371
|
+
settings = Legion::Cache::Settings.connection_options(shared_settings, @connection_options || {})
|
|
372
|
+
@shared_adapter.instance_method(:client).bind_call(self, **settings, logger: log)
|
|
373
|
+
unless reconnect_enabled?
|
|
374
|
+
close_existing_shared_client
|
|
375
|
+
select_local_fallback
|
|
376
|
+
return false
|
|
377
|
+
end
|
|
449
378
|
|
|
450
|
-
|
|
451
|
-
|
|
379
|
+
@using_local.make_false
|
|
380
|
+
cache_mark_connected(true)
|
|
381
|
+
true
|
|
382
|
+
rescue StandardError => e
|
|
383
|
+
close_existing_shared_client
|
|
384
|
+
select_local_fallback
|
|
385
|
+
raise e
|
|
452
386
|
end
|
|
453
387
|
|
|
454
388
|
def configure_shared_adapter!(requested_driver = nil)
|
|
455
|
-
driver = Legion::Cache::Settings.normalize_driver(requested_driver || configured_shared_driver)
|
|
456
|
-
return if @active_shared_driver == driver
|
|
389
|
+
driver = Legion::Cache::Settings.normalize_driver(requested_driver || @connection_options&.dig(:driver) || configured_shared_driver)
|
|
390
|
+
return if @active_shared_driver == driver && @shared_adapter
|
|
457
391
|
|
|
458
392
|
close_existing_shared_client
|
|
459
|
-
|
|
393
|
+
@shared_adapter = build_shared_adapter(driver)
|
|
394
|
+
extend @shared_adapter
|
|
460
395
|
|
|
461
396
|
@active_shared_driver = driver
|
|
462
|
-
log.info "Legion::Cache selected shared adapter=#{driver}"
|
|
463
397
|
end
|
|
464
398
|
|
|
465
399
|
def configured_shared_driver
|
|
466
|
-
|
|
467
|
-
Legion::Cache::Settings.normalize_driver(Legion::Settings.dig(:cache, :driver) || Legion::Cache::Settings.driver)
|
|
468
|
-
else
|
|
469
|
-
Legion::Cache::Settings.driver
|
|
470
|
-
end
|
|
471
|
-
rescue StandardError => e
|
|
472
|
-
handle_exception(e, level: :warn, handled: true, operation: :cache_configured_shared_driver)
|
|
473
|
-
'dalli'
|
|
400
|
+
Legion::Cache::Settings.normalize_driver(shared_settings[:driver] || Legion::Cache::Settings.driver)
|
|
474
401
|
end
|
|
475
402
|
|
|
476
403
|
def build_shared_adapter(driver)
|
|
477
|
-
|
|
478
|
-
when 'redis'
|
|
479
|
-
Legion::Cache::Redis.dup
|
|
480
|
-
else
|
|
481
|
-
Legion::Cache::Memcached.dup
|
|
482
|
-
end
|
|
404
|
+
driver == 'redis' ? Legion::Cache::Redis.dup : Legion::Cache::Memcached.dup
|
|
483
405
|
end
|
|
484
406
|
|
|
485
407
|
def close_existing_shared_client
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
408
|
+
pool = @client
|
|
409
|
+
@client = nil
|
|
410
|
+
pool&.shutdown(&:close)
|
|
489
411
|
rescue StandardError => e
|
|
490
412
|
handle_exception(e, level: :warn, handled: true, operation: :cache_close_existing_shared_client)
|
|
491
|
-
ensure
|
|
492
|
-
@client = nil
|
|
493
|
-
@connected = Concurrent::AtomicBoolean.new(false)
|
|
494
413
|
end
|
|
495
414
|
|
|
496
|
-
def
|
|
497
|
-
return
|
|
415
|
+
def shared_settings
|
|
416
|
+
return Legion::Cache::Settings.default unless defined?(Legion::Settings)
|
|
498
417
|
|
|
499
|
-
|
|
500
|
-
rescue StandardError
|
|
501
|
-
[]
|
|
502
|
-
end
|
|
503
|
-
|
|
504
|
-
def safe_pool_size
|
|
505
|
-
return 1 if using_memory?
|
|
506
|
-
return 0 unless connected?
|
|
507
|
-
|
|
508
|
-
pool_size
|
|
509
|
-
rescue StandardError
|
|
510
|
-
0
|
|
418
|
+
Legion::Settings[:cache] || Legion::Cache::Settings.default
|
|
511
419
|
end
|
|
512
420
|
|
|
513
|
-
def
|
|
514
|
-
|
|
515
|
-
return 0 unless connected?
|
|
516
|
-
|
|
517
|
-
available
|
|
518
|
-
rescue StandardError
|
|
519
|
-
0
|
|
520
|
-
end
|
|
521
|
-
|
|
522
|
-
def async_writer_pool_size
|
|
523
|
-
async_writer.pool_size
|
|
524
|
-
rescue StandardError
|
|
525
|
-
0
|
|
526
|
-
end
|
|
527
|
-
|
|
528
|
-
def async_writer_queue_depth
|
|
529
|
-
async_writer.queue_depth
|
|
530
|
-
rescue StandardError
|
|
531
|
-
0
|
|
532
|
-
end
|
|
533
|
-
|
|
534
|
-
def async_writer_processed_count
|
|
535
|
-
async_writer.processed_count
|
|
536
|
-
rescue StandardError
|
|
537
|
-
0
|
|
421
|
+
def resolved_servers
|
|
422
|
+
using_memory? ? [] : Array(shared_settings[:servers])
|
|
538
423
|
end
|
|
539
424
|
|
|
540
425
|
def configured_shutdown_timeout
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
Legion::Settings.dig(:cache, :async, :shutdown_timeout) || 5
|
|
544
|
-
rescue StandardError
|
|
545
|
-
5
|
|
546
|
-
end
|
|
547
|
-
|
|
548
|
-
def async_writer_failed_count
|
|
549
|
-
async_writer.failed_count
|
|
550
|
-
rescue StandardError
|
|
551
|
-
0
|
|
426
|
+
shared_settings.dig(:async, :shutdown_timeout) || 5
|
|
552
427
|
end
|
|
553
428
|
|
|
554
|
-
def
|
|
555
|
-
|
|
429
|
+
def reconnect_enabled?
|
|
430
|
+
enabled? && shared_settings.dig(:reconnect, :enabled) != false
|
|
556
431
|
end
|
|
557
432
|
|
|
558
433
|
def start_reconnector
|
|
559
|
-
return unless
|
|
434
|
+
return unless reconnect_enabled?
|
|
435
|
+
return if @reconnector&.running?
|
|
560
436
|
|
|
561
|
-
stop_reconnector
|
|
562
437
|
@reconnector = Legion::Cache::Reconnector.new(
|
|
563
|
-
tier:
|
|
564
|
-
|
|
565
|
-
enabled_block: -> { enabled? },
|
|
566
|
-
settings_key: :cache
|
|
438
|
+
tier: :shared, connect_block: -> { reconnect_shared! },
|
|
439
|
+
enabled_block: -> { reconnect_enabled? }, settings_key: :cache
|
|
567
440
|
)
|
|
568
441
|
@reconnector.start
|
|
569
|
-
log.info 'Legion::Cache started background reconnector for shared tier'
|
|
570
442
|
end
|
|
571
443
|
|
|
572
444
|
def stop_reconnector
|
|
573
445
|
@reconnector&.stop
|
|
574
446
|
@reconnector = nil
|
|
575
447
|
end
|
|
576
|
-
|
|
577
|
-
def uptime_seconds
|
|
578
|
-
return 0 unless @setup_at
|
|
579
|
-
|
|
580
|
-
(Time.now - @setup_at).to_i
|
|
581
|
-
rescue StandardError
|
|
582
|
-
0
|
|
583
|
-
end
|
|
584
448
|
end
|
|
585
449
|
end
|
|
586
450
|
end
|