legion-cache 1.3.20 → 1.3.21
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/CHANGELOG.md +13 -0
- data/legion-cache.gemspec +1 -1
- data/lib/legion/cache/cacheable.rb +17 -9
- data/lib/legion/cache/helper.rb +27 -33
- data/lib/legion/cache/local.rb +45 -12
- data/lib/legion/cache/memcached.rb +77 -11
- data/lib/legion/cache/memory.rb +27 -5
- data/lib/legion/cache/pool.rb +27 -4
- data/lib/legion/cache/redis.rb +42 -23
- data/lib/legion/cache/redis_hash.rb +23 -8
- data/lib/legion/cache/settings.rb +65 -10
- data/lib/legion/cache/version.rb +1 -1
- data/lib/legion/cache.rb +206 -19
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 02b45cdf2162b9906e9e51c9ba9dbc205b38b65813844100223b0985945352da
|
|
4
|
+
data.tar.gz: 2a590f3f1e8d615165955c8ded6ed35c3057abb308bcef04b5d97fd025351800
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5938e92b24a66f18169f00544aa96c3c88d80ca820807cd2ee8f08d6308fc4cd939f01e5e47d8b5ad0537c0a1ea450515641f21ad582c8726c92be0516511cec
|
|
7
|
+
data.tar.gz: 04de2781334fb61676f5943a39c85a34ddf29db2f5cbec83b9afa9b9b08b0b9e2bd532667ceef3e2e759fb821a063d4a12cf27b592860da098bdf6290dd55483
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.3.21] - 2026-04-02
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Preserve `fetch` block behavior across shared, local, memory, and Redis cache paths; local-cache failures now fall back to the in-process cache and cached `false` values are retained correctly
|
|
9
|
+
- Move shared adapter selection to runtime setup/client calls, register cache defaults through `Legion::Cache::Settings`, and normalize IPv4/hostname/IPv6 server addresses consistently
|
|
10
|
+
- Restrict Redis hash/sorted-set helpers to the actual Redis backend and enforce documented TTL behavior for helper batch writes
|
|
11
|
+
- Make the default `bundle exec rspec` suite hermetic by excluding service-backed integration specs unless `RUN_INTEGRATION_SPECS=1`
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Uplift cache logging internals to `Legion::Logging::Helper`, replacing direct logger calls with helper-provided `log` usage across cache runtime modules
|
|
15
|
+
- Route rescued cache adapter/helper/setup failures through `handle_exception` and expand runtime `info`/`debug`/`error` coverage for shared, local, memory, pool, and RedisHash flows
|
|
16
|
+
- Require `legion-logging >= 1.5.0` at runtime so helper exception handling is always available
|
|
17
|
+
|
|
5
18
|
## [1.3.20] - 2026-03-31
|
|
6
19
|
|
|
7
20
|
### Fixed
|
data/legion-cache.gemspec
CHANGED
|
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
|
|
|
28
28
|
|
|
29
29
|
spec.add_dependency 'connection_pool', '>= 2.4'
|
|
30
30
|
spec.add_dependency 'dalli', '>= 3.0'
|
|
31
|
-
spec.add_dependency 'legion-logging', '>= 1.
|
|
31
|
+
spec.add_dependency 'legion-logging', '>= 1.5.0'
|
|
32
32
|
spec.add_dependency 'legion-settings', '>= 1.3.12'
|
|
33
33
|
spec.add_dependency 'redis', '>= 5.0'
|
|
34
34
|
end
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'digest'
|
|
4
|
+
require 'legion/logging/helper'
|
|
4
5
|
|
|
5
6
|
module Legion
|
|
6
7
|
module Cache
|
|
7
8
|
module Cacheable
|
|
9
|
+
extend Legion::Logging::Helper
|
|
10
|
+
|
|
11
|
+
LOCAL_CACHE_MISS = Object.new
|
|
12
|
+
|
|
8
13
|
def self.extended(base)
|
|
9
14
|
base.instance_variable_set(:@cached_methods, {})
|
|
10
15
|
end
|
|
@@ -29,9 +34,9 @@ module Legion
|
|
|
29
34
|
unless bypass_local_method_cache
|
|
30
35
|
cached = Legion::Cache::Cacheable.cache_read(key, scope: config[:scope])
|
|
31
36
|
if cached.nil?
|
|
32
|
-
Legion::
|
|
37
|
+
Legion::Cache::Cacheable.log.debug { "[cacheable] miss key=#{key}" }
|
|
33
38
|
else
|
|
34
|
-
Legion::
|
|
39
|
+
Legion::Cache::Cacheable.log.debug { "[cacheable] hit key=#{key}" }
|
|
35
40
|
return cached
|
|
36
41
|
end
|
|
37
42
|
end
|
|
@@ -58,7 +63,8 @@ module Legion
|
|
|
58
63
|
|
|
59
64
|
memory_read(key)
|
|
60
65
|
else
|
|
61
|
-
|
|
66
|
+
result = local_cache_read(key)
|
|
67
|
+
result.equal?(LOCAL_CACHE_MISS) ? memory_read(key) : result
|
|
62
68
|
end
|
|
63
69
|
end
|
|
64
70
|
|
|
@@ -72,7 +78,8 @@ module Legion
|
|
|
72
78
|
end
|
|
73
79
|
else
|
|
74
80
|
if local_cache_available?
|
|
75
|
-
local_cache_write(key, value, ttl)
|
|
81
|
+
result = local_cache_write(key, value, ttl)
|
|
82
|
+
memory_write(key, value, ttl) unless result
|
|
76
83
|
else
|
|
77
84
|
memory_write(key, value, ttl)
|
|
78
85
|
end
|
|
@@ -88,12 +95,13 @@ module Legion
|
|
|
88
95
|
end
|
|
89
96
|
|
|
90
97
|
def self.local_cache_read(key)
|
|
91
|
-
return
|
|
98
|
+
return LOCAL_CACHE_MISS unless local_cache_available?
|
|
92
99
|
|
|
93
|
-
Legion::Cache::Local.get(key)
|
|
100
|
+
result = Legion::Cache::Local.get(key)
|
|
101
|
+
result.nil? ? LOCAL_CACHE_MISS : result
|
|
94
102
|
rescue StandardError => e
|
|
95
|
-
|
|
96
|
-
|
|
103
|
+
handle_exception(e, level: :warn, operation: :local_cache_read, key: key)
|
|
104
|
+
LOCAL_CACHE_MISS
|
|
97
105
|
end
|
|
98
106
|
|
|
99
107
|
def self.local_cache_write(key, value, ttl)
|
|
@@ -101,7 +109,7 @@ module Legion
|
|
|
101
109
|
|
|
102
110
|
Legion::Cache::Local.set(key, value, ttl)
|
|
103
111
|
rescue StandardError => e
|
|
104
|
-
|
|
112
|
+
handle_exception(e, level: :warn, operation: :local_cache_write, key: key, ttl: ttl)
|
|
105
113
|
nil
|
|
106
114
|
end
|
|
107
115
|
|
data/lib/legion/cache/helper.rb
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'legion/logging/helper'
|
|
4
|
+
|
|
3
5
|
module Legion
|
|
4
6
|
module Cache
|
|
5
7
|
module Helper
|
|
8
|
+
include Legion::Logging::Helper
|
|
9
|
+
|
|
6
10
|
FALLBACK_TTL = 60
|
|
7
11
|
|
|
8
12
|
# --- TTL Resolution ---
|
|
@@ -12,7 +16,8 @@ module Legion
|
|
|
12
16
|
return FALLBACK_TTL unless defined?(Legion::Settings)
|
|
13
17
|
|
|
14
18
|
Legion::Settings.dig(:cache, :default_ttl) || FALLBACK_TTL
|
|
15
|
-
rescue StandardError
|
|
19
|
+
rescue StandardError => e
|
|
20
|
+
handle_exception(e, level: :warn, handled: true, operation: :cache_default_ttl)
|
|
16
21
|
FALLBACK_TTL
|
|
17
22
|
end
|
|
18
23
|
|
|
@@ -20,7 +25,8 @@ module Legion
|
|
|
20
25
|
return cache_default_ttl unless defined?(Legion::Settings)
|
|
21
26
|
|
|
22
27
|
Legion::Settings.dig(:cache_local, :default_ttl) || cache_default_ttl
|
|
23
|
-
rescue StandardError
|
|
28
|
+
rescue StandardError => e
|
|
29
|
+
handle_exception(e, level: :warn, handled: true, operation: :local_cache_default_ttl)
|
|
24
30
|
cache_default_ttl
|
|
25
31
|
end
|
|
26
32
|
|
|
@@ -84,13 +90,8 @@ module Legion
|
|
|
84
90
|
|
|
85
91
|
effective_ttl = ttl || cache_default_ttl
|
|
86
92
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
Legion::Cache.mset(namespaced)
|
|
90
|
-
else
|
|
91
|
-
hash.each { |k, v| Legion::Cache.set(cache_namespace + k, v, effective_ttl) }
|
|
92
|
-
true
|
|
93
|
-
end
|
|
93
|
+
hash.each { |k, v| Legion::Cache.set(cache_namespace + k, v, effective_ttl) }
|
|
94
|
+
true
|
|
94
95
|
rescue StandardError => e
|
|
95
96
|
log_cache_error('cache_mset', e)
|
|
96
97
|
false
|
|
@@ -102,13 +103,7 @@ module Legion
|
|
|
102
103
|
keys = keys.flatten
|
|
103
104
|
return {} if keys.empty?
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
namespaced = keys.map { |k| cache_namespace + k }
|
|
107
|
-
raw = Legion::Cache::Local.mget(*namespaced)
|
|
108
|
-
keys.to_h { |k| [k, raw[cache_namespace + k]] }
|
|
109
|
-
else
|
|
110
|
-
keys.to_h { |k| [k, Legion::Cache::Local.get(cache_namespace + k)] }
|
|
111
|
-
end
|
|
106
|
+
keys.to_h { |k| [k, Legion::Cache::Local.get(cache_namespace + k)] }
|
|
112
107
|
rescue StandardError => e
|
|
113
108
|
log_cache_error('local_cache_mget', e)
|
|
114
109
|
{}
|
|
@@ -119,13 +114,8 @@ module Legion
|
|
|
119
114
|
|
|
120
115
|
effective_ttl = ttl || local_cache_default_ttl
|
|
121
116
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
Legion::Cache::Local.mset(namespaced)
|
|
125
|
-
else
|
|
126
|
-
hash.each { |k, v| Legion::Cache::Local.set(cache_namespace + k, v, effective_ttl) }
|
|
127
|
-
true
|
|
128
|
-
end
|
|
117
|
+
hash.each { |k, v| Legion::Cache::Local.set(cache_namespace + k, v, effective_ttl) }
|
|
118
|
+
true
|
|
129
119
|
rescue StandardError => e
|
|
130
120
|
log_cache_error('local_cache_mset', e)
|
|
131
121
|
false
|
|
@@ -251,7 +241,8 @@ module Legion
|
|
|
251
241
|
return 0 unless cache_connected?
|
|
252
242
|
|
|
253
243
|
Legion::Cache.pool_size
|
|
254
|
-
rescue StandardError
|
|
244
|
+
rescue StandardError => e
|
|
245
|
+
handle_exception(e, level: :warn, handled: true, operation: :cache_pool_size)
|
|
255
246
|
0
|
|
256
247
|
end
|
|
257
248
|
|
|
@@ -259,7 +250,8 @@ module Legion
|
|
|
259
250
|
return 0 unless cache_connected?
|
|
260
251
|
|
|
261
252
|
Legion::Cache.available
|
|
262
|
-
rescue StandardError
|
|
253
|
+
rescue StandardError => e
|
|
254
|
+
handle_exception(e, level: :warn, handled: true, operation: :cache_pool_available)
|
|
263
255
|
0
|
|
264
256
|
end
|
|
265
257
|
|
|
@@ -267,7 +259,8 @@ module Legion
|
|
|
267
259
|
return 0 unless local_cache_connected?
|
|
268
260
|
|
|
269
261
|
Legion::Cache::Local.pool_size
|
|
270
|
-
rescue StandardError
|
|
262
|
+
rescue StandardError => e
|
|
263
|
+
handle_exception(e, level: :warn, handled: true, operation: :local_cache_pool_size)
|
|
271
264
|
0
|
|
272
265
|
end
|
|
273
266
|
|
|
@@ -275,7 +268,8 @@ module Legion
|
|
|
275
268
|
return 0 unless local_cache_connected?
|
|
276
269
|
|
|
277
270
|
Legion::Cache::Local.available
|
|
278
|
-
rescue StandardError
|
|
271
|
+
rescue StandardError => e
|
|
272
|
+
handle_exception(e, level: :warn, handled: true, operation: :local_cache_pool_available)
|
|
279
273
|
0
|
|
280
274
|
end
|
|
281
275
|
|
|
@@ -310,8 +304,9 @@ module Legion
|
|
|
310
304
|
|
|
311
305
|
def local_cache_redis?
|
|
312
306
|
defined?(Legion::Cache::Local) &&
|
|
313
|
-
Legion::Cache::Local.
|
|
314
|
-
Legion::Cache::Local.
|
|
307
|
+
Legion::Cache::Local.connected? &&
|
|
308
|
+
Legion::Cache::Local.respond_to?(:driver_name) &&
|
|
309
|
+
Legion::Cache::Local.driver_name == 'redis'
|
|
315
310
|
end
|
|
316
311
|
|
|
317
312
|
def memcached_hash_merge(full_key, new_fields)
|
|
@@ -328,7 +323,8 @@ module Legion
|
|
|
328
323
|
parsed = Legion::JSON.load(raw)
|
|
329
324
|
# Legion::JSON.load returns symbol keys; convert to string keys to mirror Redis hgetall
|
|
330
325
|
parsed.transform_keys(&:to_s)
|
|
331
|
-
rescue StandardError
|
|
326
|
+
rescue StandardError => e
|
|
327
|
+
handle_exception(e, level: :warn, handled: true, operation: :memcached_hash_load, key: full_key)
|
|
332
328
|
nil
|
|
333
329
|
end
|
|
334
330
|
|
|
@@ -349,9 +345,7 @@ module Legion
|
|
|
349
345
|
end
|
|
350
346
|
|
|
351
347
|
def log_cache_error(method, error)
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
Legion::Logging.warn "[cache:helper] #{method} failed: #{error.class} — #{error.message}"
|
|
348
|
+
handle_exception(error, level: :warn, operation: method)
|
|
355
349
|
end
|
|
356
350
|
end
|
|
357
351
|
end
|
data/lib/legion/cache/local.rb
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'legion/logging/helper'
|
|
3
4
|
require 'legion/cache/settings'
|
|
4
5
|
|
|
5
6
|
module Legion
|
|
6
7
|
module Cache
|
|
7
8
|
module Local
|
|
8
9
|
class << self
|
|
10
|
+
include Legion::Logging::Helper
|
|
11
|
+
|
|
9
12
|
def setup(**)
|
|
10
13
|
return if @connected
|
|
11
14
|
|
|
@@ -13,22 +16,24 @@ module Legion
|
|
|
13
16
|
return unless settings[:enabled]
|
|
14
17
|
|
|
15
18
|
driver_name = settings[:driver] || Legion::Cache::Settings.driver
|
|
19
|
+
@driver_name = Legion::Cache::Settings.normalize_driver(driver_name)
|
|
16
20
|
@driver = build_driver(driver_name)
|
|
17
|
-
@driver.client(**settings, **)
|
|
21
|
+
@driver.client(**settings, logger: log, **)
|
|
18
22
|
@connected = true
|
|
19
23
|
servers = Array(settings[:servers]).join(', ')
|
|
20
|
-
|
|
24
|
+
log.info "Legion::Cache::Local connected (#{driver_name}) to #{servers}"
|
|
21
25
|
rescue StandardError => e
|
|
22
|
-
|
|
26
|
+
handle_exception(e, level: :warn, handled: true, operation: :cache_local_setup, driver: driver_name)
|
|
23
27
|
@connected = false
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
def shutdown
|
|
27
31
|
return unless @connected
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
log.info 'Shutting down Legion::Cache::Local'
|
|
30
34
|
@driver&.close
|
|
31
35
|
@driver = nil
|
|
36
|
+
@driver_name = nil
|
|
32
37
|
@connected = false
|
|
33
38
|
end
|
|
34
39
|
|
|
@@ -36,34 +41,53 @@ module Legion
|
|
|
36
41
|
@connected == true
|
|
37
42
|
end
|
|
38
43
|
|
|
44
|
+
def driver_name
|
|
45
|
+
@driver_name || Legion::Cache::Settings.normalize_driver(local_settings[:driver] || Legion::Cache::Settings.driver)
|
|
46
|
+
end
|
|
47
|
+
|
|
39
48
|
def get(key)
|
|
40
49
|
result = @driver.get(key)
|
|
41
|
-
|
|
50
|
+
log.debug "[cache:local] GET #{key} hit=#{!result.nil?}"
|
|
42
51
|
result
|
|
52
|
+
rescue StandardError => e
|
|
53
|
+
handle_exception(e, level: :warn, handled: false, operation: :cache_local_get, key: key)
|
|
54
|
+
raise
|
|
43
55
|
end
|
|
44
56
|
|
|
45
57
|
def set(key, value, ttl = 180)
|
|
46
58
|
result = @driver.set(key, value, ttl)
|
|
47
|
-
|
|
59
|
+
log.debug "[cache:local] SET #{key} ttl=#{ttl} success=#{result}"
|
|
48
60
|
result
|
|
61
|
+
rescue StandardError => e
|
|
62
|
+
handle_exception(e, level: :warn, handled: false, operation: :cache_local_set, key: key, ttl: ttl)
|
|
63
|
+
raise
|
|
49
64
|
end
|
|
50
65
|
|
|
51
|
-
def fetch(key, ttl = nil)
|
|
52
|
-
result = @driver.fetch(key, ttl)
|
|
53
|
-
|
|
66
|
+
def fetch(key, ttl = nil, &)
|
|
67
|
+
result = @driver.fetch(key, ttl, &)
|
|
68
|
+
log.debug "[cache:local] FETCH #{key} hit=#{!result.nil?}"
|
|
54
69
|
result
|
|
70
|
+
rescue StandardError => e
|
|
71
|
+
handle_exception(e, level: :warn, handled: false, operation: :cache_local_fetch, key: key, ttl: ttl)
|
|
72
|
+
raise
|
|
55
73
|
end
|
|
56
74
|
|
|
57
75
|
def delete(key)
|
|
58
76
|
result = @driver.delete(key)
|
|
59
|
-
|
|
77
|
+
log.debug "[cache:local] DELETE #{key} success=#{result}"
|
|
60
78
|
result
|
|
79
|
+
rescue StandardError => e
|
|
80
|
+
handle_exception(e, level: :warn, handled: false, operation: :cache_local_delete, key: key)
|
|
81
|
+
raise
|
|
61
82
|
end
|
|
62
83
|
|
|
63
84
|
def flush(delay = 0)
|
|
64
85
|
result = @driver.flush(delay)
|
|
65
|
-
|
|
86
|
+
log.debug '[cache:local] FLUSH completed'
|
|
66
87
|
result
|
|
88
|
+
rescue StandardError => e
|
|
89
|
+
handle_exception(e, level: :warn, handled: false, operation: :cache_local_flush, delay: delay)
|
|
90
|
+
raise
|
|
67
91
|
end
|
|
68
92
|
|
|
69
93
|
def client
|
|
@@ -72,13 +96,19 @@ module Legion
|
|
|
72
96
|
|
|
73
97
|
def close
|
|
74
98
|
@driver&.close
|
|
99
|
+
@driver = nil
|
|
100
|
+
@driver_name = nil
|
|
75
101
|
@connected = false
|
|
102
|
+
log.info 'Legion::Cache::Local pool closed'
|
|
103
|
+
@connected
|
|
76
104
|
end
|
|
77
105
|
|
|
78
106
|
def restart(**opts)
|
|
79
107
|
settings = local_settings
|
|
80
|
-
@driver&.restart(**settings.merge(opts))
|
|
108
|
+
@driver&.restart(**settings.merge(opts, logger: log))
|
|
81
109
|
@connected = true
|
|
110
|
+
log.info 'Legion::Cache::Local pool restarted'
|
|
111
|
+
@connected
|
|
82
112
|
end
|
|
83
113
|
|
|
84
114
|
def size
|
|
@@ -99,7 +129,10 @@ module Legion
|
|
|
99
129
|
|
|
100
130
|
def reset!
|
|
101
131
|
@driver = nil
|
|
132
|
+
@driver_name = nil
|
|
102
133
|
@connected = false
|
|
134
|
+
log.debug 'Legion::Cache::Local state reset'
|
|
135
|
+
@connected
|
|
103
136
|
end
|
|
104
137
|
|
|
105
138
|
private
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'openssl'
|
|
4
4
|
require 'dalli'
|
|
5
|
+
require 'legion/logging/helper'
|
|
5
6
|
require 'legion/cache/pool'
|
|
6
7
|
|
|
7
8
|
module Legion
|
|
@@ -9,12 +10,14 @@ module Legion
|
|
|
9
10
|
module Memcached
|
|
10
11
|
include Legion::Cache::Pool
|
|
11
12
|
extend self
|
|
13
|
+
extend Legion::Logging::Helper
|
|
12
14
|
|
|
13
|
-
def client(server: nil, servers: nil, **opts)
|
|
15
|
+
def client(server: nil, servers: nil, logger: nil, **opts)
|
|
14
16
|
return @client unless @client.nil?
|
|
15
17
|
|
|
16
18
|
settings = defined?(Legion::Settings) ? Legion::Settings[:cache] : {}
|
|
17
19
|
servers ||= settings[:servers] || []
|
|
20
|
+
@component_logger = logger || log
|
|
18
21
|
|
|
19
22
|
@pool_size = opts.key?(:pool_size) ? opts[:pool_size] : settings[:pool_size] || 10
|
|
20
23
|
@timeout = opts.key?(:timeout) ? opts[:timeout] : settings[:timeout] || 5
|
|
@@ -23,7 +26,7 @@ module Legion
|
|
|
23
26
|
driver: 'memcached', server: server, servers: Array(servers)
|
|
24
27
|
)
|
|
25
28
|
|
|
26
|
-
Dalli.logger =
|
|
29
|
+
Dalli.logger = shared_dalli_logger
|
|
27
30
|
cache_opts = settings.merge(opts)
|
|
28
31
|
cache_opts[:value_max_bytes] ||= 8 * 1024 * 1024
|
|
29
32
|
cache_opts[:serializer] ||= Legion::JSON
|
|
@@ -36,40 +39,103 @@ module Legion
|
|
|
36
39
|
end
|
|
37
40
|
|
|
38
41
|
@connected = true
|
|
39
|
-
|
|
42
|
+
cache_logger.info "Memcached connected to #{resolved.join(', ')}"
|
|
40
43
|
@client
|
|
44
|
+
rescue StandardError => e
|
|
45
|
+
handle_exception(e, level: :error, handled: false, operation: :memcached_client,
|
|
46
|
+
server: server, servers: Array(servers))
|
|
47
|
+
@connected = false
|
|
48
|
+
raise
|
|
41
49
|
end
|
|
42
50
|
|
|
43
51
|
def get(key)
|
|
44
52
|
result = client.with { |conn| conn.get(key) }
|
|
45
|
-
|
|
53
|
+
cache_logger.debug "[cache] GET #{key} hit=#{!result.nil?}"
|
|
46
54
|
result
|
|
55
|
+
rescue StandardError => e
|
|
56
|
+
handle_exception(e, level: :warn, handled: false, operation: :memcached_get, key: key)
|
|
57
|
+
raise
|
|
47
58
|
end
|
|
48
59
|
|
|
49
|
-
def fetch(key, ttl = nil)
|
|
50
|
-
result = client.with
|
|
51
|
-
|
|
60
|
+
def fetch(key, ttl = nil, &)
|
|
61
|
+
result = client.with do |conn|
|
|
62
|
+
if block_given?
|
|
63
|
+
conn.fetch(key, ttl, &)
|
|
64
|
+
else
|
|
65
|
+
conn.fetch(key, ttl)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
cache_logger.debug "[cache] FETCH #{key} hit=#{!result.nil?}"
|
|
52
69
|
result
|
|
70
|
+
rescue StandardError => e
|
|
71
|
+
handle_exception(e, level: :warn, handled: false, operation: :memcached_fetch, key: key, ttl: ttl)
|
|
72
|
+
raise
|
|
53
73
|
end
|
|
54
74
|
|
|
55
75
|
def set(key, value, ttl = 180)
|
|
56
76
|
result = client.with { |conn| conn.set(key, value, ttl).positive? }
|
|
57
|
-
|
|
77
|
+
cache_logger.debug "[cache] SET #{key} ttl=#{ttl} success=#{result} value_class=#{value.class}"
|
|
58
78
|
result
|
|
79
|
+
rescue StandardError => e
|
|
80
|
+
handle_exception(e, level: :warn, handled: false, operation: :memcached_set, key: key, ttl: ttl)
|
|
81
|
+
raise
|
|
59
82
|
end
|
|
60
83
|
|
|
61
84
|
def delete(key)
|
|
62
85
|
result = client.with { |conn| conn.delete(key) == true }
|
|
63
|
-
|
|
86
|
+
cache_logger.debug "[cache] DELETE #{key} success=#{result}"
|
|
64
87
|
result
|
|
88
|
+
rescue StandardError => e
|
|
89
|
+
handle_exception(e, level: :warn, handled: false, operation: :memcached_delete, key: key)
|
|
90
|
+
raise
|
|
65
91
|
end
|
|
66
92
|
|
|
67
93
|
def flush(delay = 0)
|
|
68
|
-
client.with { |conn| conn.flush(delay).first }
|
|
94
|
+
result = client.with { |conn| conn.flush(delay).first }
|
|
95
|
+
cache_logger.debug '[cache] FLUSH completed'
|
|
96
|
+
result
|
|
97
|
+
rescue StandardError => e
|
|
98
|
+
handle_exception(e, level: :warn, handled: false, operation: :memcached_flush, delay: delay)
|
|
99
|
+
raise
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def mget(*keys)
|
|
103
|
+
keys = keys.flatten
|
|
104
|
+
return {} if keys.empty?
|
|
105
|
+
|
|
106
|
+
result = client.with { |conn| conn.get_multi(*keys) }
|
|
107
|
+
cache_logger.debug "[cache] MGET keys=#{keys.size} hits=#{result.size}"
|
|
108
|
+
result
|
|
109
|
+
rescue StandardError => e
|
|
110
|
+
handle_exception(e, level: :warn, handled: false, operation: :memcached_mget, key_count: keys.size)
|
|
111
|
+
raise
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def mset(hash)
|
|
115
|
+
return true if hash.empty?
|
|
116
|
+
|
|
117
|
+
client.with { |conn| conn.set_multi(hash) }
|
|
118
|
+
cache_logger.debug "[cache] MSET keys=#{hash.size}"
|
|
119
|
+
true
|
|
120
|
+
rescue StandardError => e
|
|
121
|
+
handle_exception(e, level: :warn, handled: false, operation: :memcached_mset, key_count: hash.size)
|
|
122
|
+
raise
|
|
69
123
|
end
|
|
70
124
|
|
|
71
125
|
private
|
|
72
126
|
|
|
127
|
+
def cache_logger
|
|
128
|
+
@component_logger || log
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def shared_dalli_logger
|
|
132
|
+
if defined?(Legion::Cache) && Legion::Cache.respond_to?(:log)
|
|
133
|
+
Legion::Cache.log
|
|
134
|
+
else
|
|
135
|
+
cache_logger
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
73
139
|
def memcached_tls_context(port:)
|
|
74
140
|
return nil unless defined?(Legion::Crypt::TLS)
|
|
75
141
|
|
|
@@ -87,7 +153,7 @@ module Legion
|
|
|
87
153
|
|
|
88
154
|
Legion::Settings[:cache][:tls] || {}
|
|
89
155
|
rescue StandardError => e
|
|
90
|
-
|
|
156
|
+
handle_exception(e, level: :warn, handled: true, operation: :memcached_tls_settings)
|
|
91
157
|
{}
|
|
92
158
|
end
|
|
93
159
|
end
|
data/lib/legion/cache/memory.rb
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'legion/logging/helper'
|
|
4
|
+
|
|
3
5
|
module Legion
|
|
4
6
|
module Cache
|
|
5
7
|
module Memory
|
|
6
8
|
extend self
|
|
9
|
+
extend Legion::Logging::Helper
|
|
7
10
|
|
|
8
11
|
@store = {}
|
|
9
12
|
@expiry = {}
|
|
@@ -12,6 +15,8 @@ module Legion
|
|
|
12
15
|
|
|
13
16
|
def setup(**)
|
|
14
17
|
@connected = true
|
|
18
|
+
log.info 'Legion::Cache::Memory connected'
|
|
19
|
+
@connected
|
|
15
20
|
end
|
|
16
21
|
|
|
17
22
|
def client(**) = self
|
|
@@ -23,14 +28,21 @@ module Legion
|
|
|
23
28
|
def get(key)
|
|
24
29
|
@mutex.synchronize do
|
|
25
30
|
expire_if_needed(key)
|
|
26
|
-
@store[key]
|
|
31
|
+
result = @store[key]
|
|
32
|
+
log.debug { "[cache:memory] GET #{key} hit=#{!result.nil?}" }
|
|
33
|
+
result
|
|
27
34
|
end
|
|
28
35
|
end
|
|
29
36
|
|
|
30
37
|
def set(key, value, ttl = nil)
|
|
31
38
|
@mutex.synchronize do
|
|
32
39
|
@store[key] = value
|
|
33
|
-
|
|
40
|
+
if ttl&.positive?
|
|
41
|
+
@expiry[key] = Time.now + ttl
|
|
42
|
+
else
|
|
43
|
+
@expiry.delete(key)
|
|
44
|
+
end
|
|
45
|
+
log.debug { "[cache:memory] SET #{key} ttl=#{ttl.inspect}" }
|
|
34
46
|
value
|
|
35
47
|
end
|
|
36
48
|
end
|
|
@@ -39,6 +51,7 @@ module Legion
|
|
|
39
51
|
val = get(key)
|
|
40
52
|
return val unless val.nil?
|
|
41
53
|
|
|
54
|
+
log.debug { "[cache:memory] FETCH #{key} miss=true" }
|
|
42
55
|
val = yield if block_given?
|
|
43
56
|
set(key, val, ttl)
|
|
44
57
|
val
|
|
@@ -46,16 +59,20 @@ module Legion
|
|
|
46
59
|
|
|
47
60
|
def delete(key)
|
|
48
61
|
@mutex.synchronize do
|
|
49
|
-
@store.delete(key)
|
|
62
|
+
removed = @store.delete(key)
|
|
50
63
|
@expiry.delete(key)
|
|
64
|
+
log.debug { "[cache:memory] DELETE #{key} success=#{!removed.nil?}" }
|
|
65
|
+
removed
|
|
51
66
|
end
|
|
52
67
|
end
|
|
53
68
|
|
|
54
69
|
def flush(_delay = 0)
|
|
55
|
-
@mutex.synchronize do
|
|
70
|
+
result = @mutex.synchronize do
|
|
56
71
|
@store.clear
|
|
57
72
|
@expiry.clear
|
|
58
73
|
end
|
|
74
|
+
log.info 'Legion::Cache::Memory flushed'
|
|
75
|
+
result
|
|
59
76
|
end
|
|
60
77
|
|
|
61
78
|
def close = nil
|
|
@@ -63,14 +80,18 @@ module Legion
|
|
|
63
80
|
def shutdown
|
|
64
81
|
flush
|
|
65
82
|
@connected = false
|
|
83
|
+
log.info 'Legion::Cache::Memory shut down'
|
|
84
|
+
@connected
|
|
66
85
|
end
|
|
67
86
|
|
|
68
87
|
def reset!
|
|
69
|
-
@mutex.synchronize do
|
|
88
|
+
result = @mutex.synchronize do
|
|
70
89
|
@store.clear
|
|
71
90
|
@expiry.clear
|
|
72
91
|
@connected = false
|
|
73
92
|
end
|
|
93
|
+
log.info 'Legion::Cache::Memory state reset'
|
|
94
|
+
result
|
|
74
95
|
end
|
|
75
96
|
|
|
76
97
|
def size = 1
|
|
@@ -83,6 +104,7 @@ module Legion
|
|
|
83
104
|
|
|
84
105
|
@store.delete(key)
|
|
85
106
|
@expiry.delete(key)
|
|
107
|
+
log.debug { "[cache:memory] EXPIRE #{key}" }
|
|
86
108
|
end
|
|
87
109
|
end
|
|
88
110
|
end
|