legion-cache 1.4.1 → 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 +97 -23
- data/lib/legion/cache/memory.rb +222 -87
- data/lib/legion/cache/pool.rb +32 -15
- data/lib/legion/cache/reconnector.rb +50 -11
- data/lib/legion/cache/redis.rb +155 -118
- 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 +222 -345
- 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,455 +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
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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)
|
|
158
|
+
end
|
|
159
|
+
|
|
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
|
|
197
163
|
|
|
198
|
-
|
|
199
|
-
|
|
164
|
+
def cas(key, ttl: nil, phi: false, &)
|
|
165
|
+
cache_operation(:cas, key, ttl: resolve_ttl(ttl, phi: phi), failure: false, &)
|
|
200
166
|
end
|
|
201
167
|
|
|
202
|
-
def fetch(key, ttl: nil
|
|
203
|
-
|
|
204
|
-
return
|
|
205
|
-
return
|
|
168
|
+
def fetch(key, ttl: nil)
|
|
169
|
+
cached = get(key)
|
|
170
|
+
return cached unless cached.nil?
|
|
171
|
+
return nil unless block_given?
|
|
206
172
|
|
|
207
|
-
|
|
208
|
-
|
|
173
|
+
value = yield
|
|
174
|
+
set_sync(key, value, ttl: ttl)
|
|
175
|
+
value
|
|
209
176
|
end
|
|
210
177
|
|
|
211
178
|
def delete(key, async: true)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
179
|
+
setup unless @initialized || connected?
|
|
180
|
+
return false unless connected?
|
|
181
|
+
|
|
182
|
+
if async
|
|
183
|
+
async_writer.enqueue(key: key) { delete_sync(key) }
|
|
215
184
|
else
|
|
216
|
-
|
|
185
|
+
delete_sync(key)
|
|
217
186
|
end
|
|
218
187
|
end
|
|
219
188
|
|
|
220
189
|
def delete_sync(key)
|
|
221
|
-
|
|
222
|
-
return Legion::Cache::Local.delete_sync(key) if using_local?
|
|
223
|
-
return Legion::Cache::Local.delete_sync(key) if failback_to_local?
|
|
224
|
-
|
|
225
|
-
configure_shared_adapter!
|
|
226
|
-
super
|
|
190
|
+
cache_operation(:delete_sync, key, failure: false)
|
|
227
191
|
end
|
|
228
192
|
|
|
229
193
|
def flush
|
|
230
|
-
|
|
231
|
-
return Legion::Cache::Local.flush if using_local?
|
|
232
|
-
return Legion::Cache::Local.flush if failback_to_local?
|
|
233
|
-
|
|
234
|
-
configure_shared_adapter!
|
|
235
|
-
super
|
|
194
|
+
cache_operation(:flush, failure: false)
|
|
236
195
|
end
|
|
237
196
|
|
|
238
197
|
def mget(*keys)
|
|
239
198
|
keys = keys.flatten
|
|
240
199
|
return {} if keys.empty?
|
|
241
|
-
return keys.to_h { |key| [key, Legion::Cache::Memory.get(key)] } if using_memory?
|
|
242
|
-
return Legion::Cache::Local.mget(*keys) if using_local?
|
|
243
|
-
return Legion::Cache::Local.mget(*keys) if failback_to_local?
|
|
244
200
|
|
|
245
|
-
|
|
246
|
-
|
|
201
|
+
result = cache_operation(:mget, *keys, failure: {})
|
|
202
|
+
return result if using_memory? || using_local?
|
|
203
|
+
|
|
204
|
+
local_cache_mget(result, keys)
|
|
247
205
|
end
|
|
248
206
|
|
|
249
|
-
def mset(hash, ttl: nil, async: true)
|
|
250
|
-
return
|
|
207
|
+
def mset(hash, ttl: nil, async: true, phi: false)
|
|
208
|
+
return mset_sync(hash, ttl: ttl, phi: phi) unless async
|
|
251
209
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
|
257
218
|
end
|
|
219
|
+
accepted
|
|
258
220
|
end
|
|
259
221
|
|
|
260
|
-
def mset_sync(hash, ttl: nil, **)
|
|
222
|
+
def mset_sync(hash, ttl: nil, phi: false, **)
|
|
261
223
|
return true if hash.empty?
|
|
262
|
-
return hash.each { |key, value| Legion::Cache::Memory.set_sync(key, value, ttl: ttl) } && true if using_memory?
|
|
263
|
-
return Legion::Cache::Local.mset(hash, ttl: ttl) if using_local?
|
|
264
|
-
return Legion::Cache::Local.mset(hash, ttl: ttl) if failback_to_local?
|
|
265
224
|
|
|
266
|
-
|
|
267
|
-
super
|
|
225
|
+
cache_operation(:mset_sync, hash, ttl: resolve_ttl(ttl, phi: phi), failure: false)
|
|
268
226
|
end
|
|
269
227
|
|
|
270
228
|
def close
|
|
271
|
-
|
|
272
|
-
Legion::Cache::Memory.shutdown
|
|
273
|
-
@using_memory.make_false
|
|
274
|
-
@connected.make_false
|
|
275
|
-
Legion::Settings[:cache][:connected] = false if defined?(Legion::Settings)
|
|
276
|
-
return false
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
if using_local?
|
|
280
|
-
Legion::Cache::Local.close
|
|
281
|
-
@using_local.make_false
|
|
282
|
-
@connected.make_false
|
|
283
|
-
Legion::Settings[:cache][:connected] = false if defined?(Legion::Settings)
|
|
284
|
-
return false
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
return false unless instance_variable_defined?(:@client) && @client
|
|
288
|
-
|
|
289
|
-
configure_shared_adapter!
|
|
290
|
-
result = super
|
|
291
|
-
@connected = Concurrent::AtomicBoolean.new(false)
|
|
292
|
-
Legion::Settings[:cache][:connected] = false if defined?(Legion::Settings)
|
|
293
|
-
result
|
|
229
|
+
shutdown
|
|
294
230
|
end
|
|
295
231
|
|
|
296
|
-
def restart(**
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
@using_local.make_false
|
|
300
|
-
result = super
|
|
301
|
-
@connected = Concurrent::AtomicBoolean.new(true)
|
|
302
|
-
Legion::Settings[:cache][:connected] = true if defined?(Legion::Settings)
|
|
303
|
-
result
|
|
232
|
+
def restart(**)
|
|
233
|
+
shutdown
|
|
234
|
+
setup(**)
|
|
304
235
|
end
|
|
305
236
|
|
|
306
237
|
def size
|
|
307
|
-
|
|
308
|
-
return Legion::Cache::Local.size if using_local?
|
|
309
|
-
|
|
310
|
-
configure_shared_adapter!
|
|
311
|
-
super
|
|
238
|
+
pool_size
|
|
312
239
|
end
|
|
313
240
|
|
|
314
241
|
def available
|
|
242
|
+
return 0 unless connected?
|
|
315
243
|
return Legion::Cache::Memory.available if using_memory?
|
|
316
244
|
return Legion::Cache::Local.available if using_local?
|
|
317
245
|
|
|
318
|
-
|
|
319
|
-
super
|
|
246
|
+
@client&.available || 0
|
|
320
247
|
end
|
|
321
248
|
|
|
322
249
|
def pool_size
|
|
250
|
+
return 0 unless connected?
|
|
323
251
|
return Legion::Cache::Memory.size if using_memory?
|
|
324
252
|
return Legion::Cache::Local.pool_size if using_local?
|
|
325
253
|
|
|
326
|
-
|
|
327
|
-
super
|
|
254
|
+
@client&.size || 0
|
|
328
255
|
end
|
|
329
256
|
|
|
330
257
|
def timeout
|
|
258
|
+
return 0 unless connected?
|
|
331
259
|
return 0 if using_memory?
|
|
332
260
|
return Legion::Cache::Local.timeout if using_local?
|
|
333
261
|
|
|
334
|
-
|
|
335
|
-
super
|
|
262
|
+
@timeout || shared_settings[:timeout] || 5
|
|
336
263
|
end
|
|
337
264
|
|
|
338
265
|
private
|
|
339
266
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
267
|
+
attr_reader :async_writer
|
|
268
|
+
|
|
269
|
+
def cache_operation(operation, *, failure:, **, &block)
|
|
270
|
+
setup unless @initialized || connected?
|
|
271
|
+
return failure unless connected?
|
|
343
272
|
|
|
344
|
-
|
|
345
|
-
return Legion::Cache::
|
|
346
|
-
return Legion::Cache::Local.set(key, value, ttl: ttl) if using_local?
|
|
347
|
-
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?
|
|
348
275
|
|
|
349
276
|
configure_shared_adapter!
|
|
350
|
-
|
|
351
|
-
|
|
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?
|
|
352
296
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
return Legion::Cache::Local.delete(key) if using_local?
|
|
356
|
-
return Legion::Cache::Local.delete(key) if failback_to_local?
|
|
297
|
+
connected? ? result : failure
|
|
298
|
+
end
|
|
357
299
|
|
|
358
|
-
|
|
359
|
-
|
|
300
|
+
def cache_mark_connected(value)
|
|
301
|
+
@connected.value = value
|
|
302
|
+
Legion::Settings[:cache][:connected] = value if defined?(Legion::Settings)
|
|
360
303
|
end
|
|
361
304
|
|
|
362
|
-
def
|
|
363
|
-
return
|
|
364
|
-
return Legion::Cache::Local.mset(hash, ttl: ttl) if using_local?
|
|
365
|
-
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)
|
|
366
307
|
|
|
367
|
-
|
|
368
|
-
|
|
308
|
+
cache_mark_connected(false)
|
|
309
|
+
select_local_fallback
|
|
310
|
+
start_reconnector
|
|
369
311
|
end
|
|
370
312
|
|
|
371
|
-
def
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
else
|
|
377
|
-
true
|
|
378
|
-
end
|
|
379
|
-
setting && (!enabled? || !connected?)
|
|
380
|
-
rescue StandardError => e
|
|
381
|
-
handle_exception(e, level: :warn, handled: true, operation: :cache_failback_check)
|
|
382
|
-
false
|
|
313
|
+
def select_local_fallback
|
|
314
|
+
available = local_fallback_available?
|
|
315
|
+
@using_local.value = available
|
|
316
|
+
cache_mark_connected(available)
|
|
317
|
+
available
|
|
383
318
|
end
|
|
384
319
|
|
|
385
320
|
def resolve_ttl(ttl, phi: false)
|
|
386
|
-
|
|
387
|
-
enforce_phi_ttl(effective, phi: phi)
|
|
321
|
+
enforce_phi_ttl(ttl || shared_settings[:default_ttl] || 3600, phi: phi)
|
|
388
322
|
end
|
|
389
323
|
|
|
390
|
-
def
|
|
391
|
-
return
|
|
324
|
+
def local_cache_get(key)
|
|
325
|
+
return nil unless local_fallback_available?
|
|
392
326
|
|
|
393
|
-
Legion::
|
|
394
|
-
rescue StandardError
|
|
395
|
-
3600
|
|
327
|
+
Legion::Cache::Local.get(key)
|
|
396
328
|
end
|
|
397
329
|
|
|
398
|
-
def
|
|
399
|
-
return
|
|
330
|
+
def local_cache_mget(shared_values, keys)
|
|
331
|
+
return shared_values unless local_fallback_available?
|
|
400
332
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
|
404
340
|
end
|
|
405
341
|
|
|
406
|
-
def
|
|
407
|
-
|
|
408
|
-
|
|
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)
|
|
409
352
|
@using_local.make_false
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
log.info "Legion::Cache connected (driver=#{driver}) to #{servers}"
|
|
353
|
+
@using_memory.make_false
|
|
354
|
+
cache_mark_connected(true)
|
|
355
|
+
true
|
|
414
356
|
rescue StandardError => e
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
@connected.make_false
|
|
423
|
-
Legion::Settings[:cache][:connected] = false
|
|
424
|
-
log.error 'Legion::Cache shared and local adapters are unavailable'
|
|
425
|
-
end
|
|
426
|
-
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
|
|
427
364
|
end
|
|
428
365
|
|
|
429
366
|
def reconnect_shared!
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
|
436
378
|
|
|
437
|
-
|
|
438
|
-
|
|
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
|
|
439
386
|
end
|
|
440
387
|
|
|
441
388
|
def configure_shared_adapter!(requested_driver = nil)
|
|
442
|
-
driver = Legion::Cache::Settings.normalize_driver(requested_driver || configured_shared_driver)
|
|
443
|
-
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
|
|
444
391
|
|
|
445
392
|
close_existing_shared_client
|
|
446
|
-
|
|
393
|
+
@shared_adapter = build_shared_adapter(driver)
|
|
394
|
+
extend @shared_adapter
|
|
447
395
|
|
|
448
396
|
@active_shared_driver = driver
|
|
449
|
-
log.info "Legion::Cache selected shared adapter=#{driver}"
|
|
450
397
|
end
|
|
451
398
|
|
|
452
399
|
def configured_shared_driver
|
|
453
|
-
|
|
454
|
-
Legion::Cache::Settings.normalize_driver(Legion::Settings.dig(:cache, :driver) || Legion::Cache::Settings.driver)
|
|
455
|
-
else
|
|
456
|
-
Legion::Cache::Settings.driver
|
|
457
|
-
end
|
|
458
|
-
rescue StandardError => e
|
|
459
|
-
handle_exception(e, level: :warn, handled: true, operation: :cache_configured_shared_driver)
|
|
460
|
-
'dalli'
|
|
400
|
+
Legion::Cache::Settings.normalize_driver(shared_settings[:driver] || Legion::Cache::Settings.driver)
|
|
461
401
|
end
|
|
462
402
|
|
|
463
403
|
def build_shared_adapter(driver)
|
|
464
|
-
|
|
465
|
-
when 'redis'
|
|
466
|
-
Legion::Cache::Redis.dup
|
|
467
|
-
else
|
|
468
|
-
Legion::Cache::Memcached.dup
|
|
469
|
-
end
|
|
404
|
+
driver == 'redis' ? Legion::Cache::Redis.dup : Legion::Cache::Memcached.dup
|
|
470
405
|
end
|
|
471
406
|
|
|
472
407
|
def close_existing_shared_client
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
408
|
+
pool = @client
|
|
409
|
+
@client = nil
|
|
410
|
+
pool&.shutdown(&:close)
|
|
476
411
|
rescue StandardError => e
|
|
477
412
|
handle_exception(e, level: :warn, handled: true, operation: :cache_close_existing_shared_client)
|
|
478
|
-
ensure
|
|
479
|
-
@client = nil
|
|
480
|
-
@connected = Concurrent::AtomicBoolean.new(false)
|
|
481
413
|
end
|
|
482
414
|
|
|
483
|
-
def
|
|
484
|
-
return
|
|
415
|
+
def shared_settings
|
|
416
|
+
return Legion::Cache::Settings.default unless defined?(Legion::Settings)
|
|
485
417
|
|
|
486
|
-
|
|
487
|
-
rescue StandardError
|
|
488
|
-
[]
|
|
489
|
-
end
|
|
490
|
-
|
|
491
|
-
def safe_pool_size
|
|
492
|
-
return 1 if using_memory?
|
|
493
|
-
return 0 unless connected?
|
|
494
|
-
|
|
495
|
-
pool_size
|
|
496
|
-
rescue StandardError
|
|
497
|
-
0
|
|
418
|
+
Legion::Settings[:cache] || Legion::Cache::Settings.default
|
|
498
419
|
end
|
|
499
420
|
|
|
500
|
-
def
|
|
501
|
-
|
|
502
|
-
return 0 unless connected?
|
|
503
|
-
|
|
504
|
-
available
|
|
505
|
-
rescue StandardError
|
|
506
|
-
0
|
|
507
|
-
end
|
|
508
|
-
|
|
509
|
-
def async_writer_pool_size
|
|
510
|
-
async_writer.pool_size
|
|
511
|
-
rescue StandardError
|
|
512
|
-
0
|
|
513
|
-
end
|
|
514
|
-
|
|
515
|
-
def async_writer_queue_depth
|
|
516
|
-
async_writer.queue_depth
|
|
517
|
-
rescue StandardError
|
|
518
|
-
0
|
|
519
|
-
end
|
|
520
|
-
|
|
521
|
-
def async_writer_processed_count
|
|
522
|
-
async_writer.processed_count
|
|
523
|
-
rescue StandardError
|
|
524
|
-
0
|
|
421
|
+
def resolved_servers
|
|
422
|
+
using_memory? ? [] : Array(shared_settings[:servers])
|
|
525
423
|
end
|
|
526
424
|
|
|
527
425
|
def configured_shutdown_timeout
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
Legion::Settings.dig(:cache, :async, :shutdown_timeout) || 5
|
|
531
|
-
rescue StandardError
|
|
532
|
-
5
|
|
533
|
-
end
|
|
534
|
-
|
|
535
|
-
def async_writer_failed_count
|
|
536
|
-
async_writer.failed_count
|
|
537
|
-
rescue StandardError
|
|
538
|
-
0
|
|
426
|
+
shared_settings.dig(:async, :shutdown_timeout) || 5
|
|
539
427
|
end
|
|
540
428
|
|
|
541
|
-
def
|
|
542
|
-
|
|
429
|
+
def reconnect_enabled?
|
|
430
|
+
enabled? && shared_settings.dig(:reconnect, :enabled) != false
|
|
543
431
|
end
|
|
544
432
|
|
|
545
433
|
def start_reconnector
|
|
546
|
-
return unless
|
|
434
|
+
return unless reconnect_enabled?
|
|
435
|
+
return if @reconnector&.running?
|
|
547
436
|
|
|
548
|
-
stop_reconnector
|
|
549
437
|
@reconnector = Legion::Cache::Reconnector.new(
|
|
550
|
-
tier:
|
|
551
|
-
|
|
552
|
-
enabled_block: -> { enabled? },
|
|
553
|
-
settings_key: :cache
|
|
438
|
+
tier: :shared, connect_block: -> { reconnect_shared! },
|
|
439
|
+
enabled_block: -> { reconnect_enabled? }, settings_key: :cache
|
|
554
440
|
)
|
|
555
441
|
@reconnector.start
|
|
556
|
-
log.info 'Legion::Cache started background reconnector for shared tier'
|
|
557
442
|
end
|
|
558
443
|
|
|
559
444
|
def stop_reconnector
|
|
560
445
|
@reconnector&.stop
|
|
561
446
|
@reconnector = nil
|
|
562
447
|
end
|
|
563
|
-
|
|
564
|
-
def uptime_seconds
|
|
565
|
-
return 0 unless @setup_at
|
|
566
|
-
|
|
567
|
-
(Time.now - @setup_at).to_i
|
|
568
|
-
rescue StandardError
|
|
569
|
-
0
|
|
570
|
-
end
|
|
571
448
|
end
|
|
572
449
|
end
|
|
573
450
|
end
|