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.
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
- return true unless defined?(Legion::Settings)
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
- @connected&.true? || false
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: driver_name,
53
- servers: resolved_servers,
54
- enabled: enabled?,
55
- connected: connected?,
56
- using_local: using_local?,
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 unless enabled?
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
- @connected.make_true
84
- Legion::Settings[:cache][:connected] = true
85
- log.info 'Legion::Cache using in-memory adapter (lite mode)'
86
- return
68
+ @using_local.make_false
69
+ cache_mark_connected(true)
70
+ return true
87
71
  end
88
72
 
89
- log.debug { "Legion::Cache setup driver=#{Legion::Settings[:cache][:driver]} servers=#{Array(Legion::Settings[:cache][:servers]).size}" }
90
- setup_local
91
- setup_shared(**)
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
- # 3. Now close pools
101
- if using_memory?
102
- Legion::Cache::Memory.shutdown
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
- @connected.make_false
110
- Legion::Settings[:cache][:connected] = false
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&.true? || false
105
+ @using_local.value
123
106
  end
124
107
 
125
108
  def using_memory?
126
- @using_memory&.true? || false
109
+ @using_memory.value
127
110
  end
128
111
 
129
- def client(**opts)
130
- if ENV['LEGION_MODE'] == 'lite'
131
- Legion::Cache::Memory.setup unless Legion::Cache::Memory.connected?
132
- @using_memory.make_true
133
- @using_local.make_false
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
- configure_shared_adapter!(opts[:driver])
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
- return Legion::Cache::Memory.get(key) if using_memory?
156
- return Legion::Cache::Local.get(key) if using_local?
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
- configure_shared_adapter!
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
- return 3600 unless defined?(Legion::Settings)
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
- Legion::Settings.dig(:cache, :compliance, :phi_max_ttl) || 3600
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
- max = phi_max_ttl
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
- effective_ttl = resolve_ttl(ttl, phi: phi)
145
+ setup unless @initialized || connected?
146
+ return false unless connected?
184
147
 
185
- if async && async_writer.running?
186
- async_writer.enqueue { set_internal(key, value, ttl: effective_ttl) }
187
- true
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
- set_internal(key, value, ttl: effective_ttl)
152
+ set_sync(key, value, ttl: effective_ttl)
190
153
  end
191
154
  end
192
155
 
193
- def set_sync(key, value, ttl: nil, **)
194
- return Legion::Cache::Memory.set_sync(key, value, ttl: ttl) if using_memory?
195
- return Legion::Cache::Local.set_sync(key, value, ttl: ttl) if using_local?
196
- return Legion::Cache::Local.set_sync(key, value, ttl: ttl) if failback_to_local?
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
- configure_shared_adapter!
199
- super
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
- return Legion::Cache::Memory.fetch(key, ttl: ttl, &) if using_memory?
204
- return Legion::Cache::Local.fetch(key, ttl: ttl, &) if using_local?
205
- return Legion::Cache::Local.fetch(key, ttl: ttl, &) if failback_to_local?
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
- configure_shared_adapter!
208
- super
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
- if async && async_writer.running?
213
- async_writer.enqueue { delete_internal(key) }
214
- true
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
- delete_internal(key)
185
+ delete_sync(key)
217
186
  end
218
187
  end
219
188
 
220
189
  def delete_sync(key)
221
- return Legion::Cache::Memory.delete_sync(key) if using_memory?
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
- return Legion::Cache::Memory.flush if using_memory?
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
- configure_shared_adapter!
246
- super
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 true if hash.empty?
207
+ def mset(hash, ttl: nil, async: true, phi: false)
208
+ return mset_sync(hash, ttl: ttl, phi: phi) unless async
251
209
 
252
- if async && async_writer.running?
253
- async_writer.enqueue { mset_internal(hash, ttl: ttl) }
254
- true
255
- else
256
- mset_internal(hash, ttl: ttl)
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
- configure_shared_adapter!
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
- if using_memory?
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(**opts)
297
- configure_shared_adapter!(opts[:driver])
298
- @using_memory.make_false
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
- return Legion::Cache::Memory.size if using_memory?
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
- configure_shared_adapter!
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
- configure_shared_adapter!
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
- configure_shared_adapter!
335
- super
262
+ @timeout || shared_settings[:timeout] || 5
336
263
  end
337
264
 
338
265
  private
339
266
 
340
- def async_writer
341
- Legion::Cache.instance_variable_get(:@async_writer)
342
- end
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
- def set_internal(key, value, ttl: nil)
345
- return Legion::Cache::Memory.set(key, value, ttl: ttl) if using_memory?
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
- set_sync(key, value, ttl: ttl)
351
- end
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
- def delete_internal(key)
354
- return Legion::Cache::Memory.delete(key) if using_memory?
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
- configure_shared_adapter!
359
- delete_sync(key)
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 mset_internal(hash, ttl: nil)
363
- return hash.each { |key, value| Legion::Cache::Memory.set(key, value, ttl: ttl) } && true if using_memory?
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
- configure_shared_adapter!
368
- mset_sync(hash, ttl: ttl)
308
+ cache_mark_connected(false)
309
+ select_local_fallback
310
+ start_reconnector
369
311
  end
370
312
 
371
- def failback_to_local?
372
- return false unless Legion::Cache::Local.connected?
373
-
374
- setting = if defined?(Legion::Settings)
375
- Legion::Settings.dig(:cache, :failback_to_local) != false
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
- effective = ttl || default_ttl
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 default_ttl
391
- return 3600 unless defined?(Legion::Settings)
324
+ def local_cache_get(key)
325
+ return nil unless local_fallback_available?
392
326
 
393
- Legion::Settings.dig(:cache, :default_ttl) || 3600
394
- rescue StandardError
395
- 3600
327
+ Legion::Cache::Local.get(key)
396
328
  end
397
329
 
398
- def setup_local
399
- return if Legion::Cache::Local.connected?
330
+ def local_cache_mget(shared_values, keys)
331
+ return shared_values unless local_fallback_available?
400
332
 
401
- Legion::Cache::Local.setup
402
- rescue StandardError => e
403
- report_exception(e, level: :warn, handled: true, operation: :setup_local)
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 setup_shared(**)
407
- client(**Legion::Settings[:cache], logger: log, **)
408
- @connected.make_true
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
- Legion::Settings[:cache][:connected] = true
411
- driver = Legion::Settings[:cache][:driver] || 'dalli'
412
- servers = Array(Legion::Settings[:cache][:servers]).join(', ')
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
- report_exception(e, level: :warn, handled: true, operation: :setup_shared, fallback: :local)
416
- if Legion::Cache::Local.connected?
417
- @using_local.make_true
418
- @connected.make_true
419
- Legion::Settings[:cache][:connected] = true
420
- log.info 'Legion::Cache fell back to Local cache'
421
- else
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
- client(**Legion::Settings[:cache], logger: log)
431
- @connected.make_true
432
- @using_local.make_false
433
- Legion::Settings[:cache][:connected] = true
434
- log.info 'Legion::Cache shared reconnected'
435
- end
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
- def report_exception(exception, level:, handled:, **)
438
- handle_exception(exception, level: level, handled: handled, **)
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
- extend build_shared_adapter(driver)
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
- if defined?(Legion::Settings)
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
- case Legion::Cache::Settings.normalize_driver(driver)
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
- return unless instance_variable_defined?(:@client) && @client
474
-
475
- @client.shutdown(&:close)
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 resolved_servers
484
- return [] if using_memory?
415
+ def shared_settings
416
+ return Legion::Cache::Settings.default unless defined?(Legion::Settings)
485
417
 
486
- Array(Legion::Settings.dig(:cache, :servers))
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 safe_pool_available
501
- return 1 if using_memory?
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
- return 5 unless defined?(Legion::Settings)
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 reconnector_attempts
542
- @reconnector&.attempts || 0
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 enabled?
434
+ return unless reconnect_enabled?
435
+ return if @reconnector&.running?
547
436
 
548
- stop_reconnector
549
437
  @reconnector = Legion::Cache::Reconnector.new(
550
- tier: :shared,
551
- connect_block: -> { reconnect_shared! },
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