legion-cache 1.1.1 → 1.3.0
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/.github/workflows/ci.yml +19 -0
- data/.gitignore +8 -1
- data/.rubocop.yml +39 -10
- data/CHANGELOG.md +18 -0
- data/CLAUDE.md +140 -0
- data/Gemfile +7 -1
- data/LICENSE +201 -0
- data/README.md +99 -11
- data/legion-cache.gemspec +22 -28
- data/lib/legion/cache/local.rb +115 -0
- data/lib/legion/cache/memcached.rb +19 -6
- data/lib/legion/cache/pool.rb +4 -0
- data/lib/legion/cache/redis.rb +3 -1
- data/lib/legion/cache/settings.rb +38 -11
- data/lib/legion/cache/version.rb +1 -1
- data/lib/legion/cache.rb +85 -13
- metadata +40 -121
- data/.circleci/config.yml +0 -113
- data/.rspec +0 -3
- data/Gemfile.lock +0 -87
- data/LICENSE.txt +0 -21
- data/Rakefile +0 -8
- data/bitbucket-pipelines.yml +0 -17
data/legion-cache.gemspec
CHANGED
|
@@ -3,38 +3,32 @@
|
|
|
3
3
|
require_relative 'lib/legion/cache/version'
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name
|
|
6
|
+
spec.name = 'legion-cache'
|
|
7
7
|
spec.version = Legion::Cache::VERSION
|
|
8
8
|
spec.authors = ['Esity']
|
|
9
9
|
spec.email = ['matthewdiverson@gmail.com']
|
|
10
10
|
|
|
11
|
-
spec.summary = '
|
|
12
|
-
spec.description = '
|
|
13
|
-
spec.homepage = 'https://
|
|
14
|
-
spec.license = '
|
|
15
|
-
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
|
16
|
-
|
|
17
|
-
spec.metadata['homepage_uri'] = spec.homepage
|
|
18
|
-
spec.metadata['source_code_uri'] = 'https://bitbucket.org/legion-io/legion-cache/src'
|
|
19
|
-
spec.metadata['changelog_uri'] = 'https://bitbucket.org/legion-io/legion-cache/src/master/CHANGELOG.md'
|
|
20
|
-
spec.metadata['wiki_uri'] = 'https://bitbucket.org/legion-io/legion-cache/wiki'
|
|
21
|
-
spec.metadata['bug_tracker_uri'] = 'https://bitbucket.org/legion-io/legion-cache/issues'
|
|
22
|
-
|
|
23
|
-
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
24
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
25
|
-
end
|
|
11
|
+
spec.summary = 'Wraps both the redis and dalli gems to make a consistent interface for accessing cached objects'
|
|
12
|
+
spec.description = 'A Wrapper class for the LegionIO framework to interface with both Memcached and Redis for caching purposes'
|
|
13
|
+
spec.homepage = 'https://github.com/LegionIO/legion-cache'
|
|
14
|
+
spec.license = 'Apache-2.0'
|
|
26
15
|
spec.require_paths = ['lib']
|
|
16
|
+
spec.required_ruby_version = '>= 3.4'
|
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
18
|
+
spec.extra_rdoc_files = %w[README.md LICENSE CHANGELOG.md]
|
|
19
|
+
spec.metadata = {
|
|
20
|
+
'bug_tracker_uri' => 'https://github.com/LegionIO/legion-cache/issues',
|
|
21
|
+
'changelog_uri' => 'https://github.com/LegionIO/legion-cache/blob/main/CHANGELOG.md',
|
|
22
|
+
'documentation_uri' => 'https://github.com/LegionIO/legion-cache',
|
|
23
|
+
'homepage_uri' => 'https://github.com/LegionIO/LegionIO',
|
|
24
|
+
'source_code_uri' => 'https://github.com/LegionIO/legion-cache',
|
|
25
|
+
'wiki_uri' => 'https://github.com/LegionIO/legion-cache/wiki',
|
|
26
|
+
'rubygems_mfa_required' => 'true'
|
|
27
|
+
}
|
|
27
28
|
|
|
28
|
-
spec.
|
|
29
|
-
spec.
|
|
30
|
-
spec.
|
|
31
|
-
spec.
|
|
32
|
-
spec.
|
|
33
|
-
spec.add_development_dependency 'rspec_junit_formatter'
|
|
34
|
-
spec.add_development_dependency 'rubocop'
|
|
35
|
-
spec.add_development_dependency 'simplecov', '< 0.18.0'
|
|
36
|
-
|
|
37
|
-
spec.add_dependency 'connection_pool', '>= 2.2.3'
|
|
38
|
-
spec.add_dependency 'dalli', '>= 2.7'
|
|
39
|
-
spec.add_dependency 'redis', '>= 4.2'
|
|
29
|
+
spec.add_dependency 'connection_pool', '>= 2.4'
|
|
30
|
+
spec.add_dependency 'dalli', '>= 3.0'
|
|
31
|
+
spec.add_dependency 'legion-logging'
|
|
32
|
+
spec.add_dependency 'legion-settings'
|
|
33
|
+
spec.add_dependency 'redis', '>= 5.0'
|
|
40
34
|
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/cache/settings'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Cache
|
|
7
|
+
module Local
|
|
8
|
+
class << self
|
|
9
|
+
def setup(**)
|
|
10
|
+
return if @connected
|
|
11
|
+
|
|
12
|
+
settings = local_settings
|
|
13
|
+
return unless settings[:enabled]
|
|
14
|
+
|
|
15
|
+
driver_name = settings[:driver] || Legion::Cache::Settings.driver
|
|
16
|
+
@driver = build_driver(driver_name)
|
|
17
|
+
@driver.client(**settings, **)
|
|
18
|
+
@connected = true
|
|
19
|
+
Legion::Logging.info "Legion::Cache::Local connected (#{driver_name})" if defined?(Legion::Logging)
|
|
20
|
+
rescue StandardError => e
|
|
21
|
+
Legion::Logging.warn "Legion::Cache::Local setup failed: #{e.message}" if defined?(Legion::Logging)
|
|
22
|
+
@connected = false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def shutdown
|
|
26
|
+
return unless @connected
|
|
27
|
+
|
|
28
|
+
Legion::Logging.info 'Shutting down Legion::Cache::Local' if defined?(Legion::Logging)
|
|
29
|
+
@driver&.close
|
|
30
|
+
@driver = nil
|
|
31
|
+
@connected = false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def connected?
|
|
35
|
+
@connected == true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def get(key)
|
|
39
|
+
@driver.get(key)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def set(key, value, ttl = 180)
|
|
43
|
+
@driver.set(key, value, ttl)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def fetch(key, ttl = nil)
|
|
47
|
+
@driver.fetch(key, ttl)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def delete(key)
|
|
51
|
+
@driver.delete(key)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def flush(delay = 0)
|
|
55
|
+
@driver.flush(delay)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def client
|
|
59
|
+
@driver&.client
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def close
|
|
63
|
+
@driver&.close
|
|
64
|
+
@connected = false
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def restart(**opts)
|
|
68
|
+
settings = local_settings
|
|
69
|
+
@driver&.restart(**settings.merge(opts))
|
|
70
|
+
@connected = true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def size
|
|
74
|
+
@driver.size
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def available
|
|
78
|
+
@driver.available
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def pool_size
|
|
82
|
+
@driver.pool_size
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def timeout
|
|
86
|
+
@driver.timeout
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def reset!
|
|
90
|
+
@driver = nil
|
|
91
|
+
@connected = false
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def build_driver(driver_name)
|
|
97
|
+
case driver_name
|
|
98
|
+
when 'redis'
|
|
99
|
+
require 'legion/cache/redis'
|
|
100
|
+
Legion::Cache::Redis.dup
|
|
101
|
+
else
|
|
102
|
+
require 'legion/cache/memcached'
|
|
103
|
+
Legion::Cache::Memcached.dup
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def local_settings
|
|
108
|
+
return Legion::Cache::Settings.local unless defined?(Legion::Settings)
|
|
109
|
+
|
|
110
|
+
Legion::Settings[:cache_local] || Legion::Cache::Settings.local
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dalli'
|
|
2
4
|
require 'legion/cache/pool'
|
|
3
5
|
|
|
@@ -5,7 +7,7 @@ module Legion
|
|
|
5
7
|
module Cache
|
|
6
8
|
module Memcached
|
|
7
9
|
include Legion::Cache::Pool
|
|
8
|
-
extend self
|
|
10
|
+
extend self # rubocop:disable Style/ModuleFunction
|
|
9
11
|
|
|
10
12
|
def client(servers: Legion::Settings[:cache][:servers], **opts)
|
|
11
13
|
return @client unless @client.nil?
|
|
@@ -14,8 +16,11 @@ module Legion
|
|
|
14
16
|
@timeout = opts.key?(:timeout) ? opts[:timeout] : Legion::Settings[:cache][:timeout] || 5
|
|
15
17
|
|
|
16
18
|
Dalli.logger = Legion::Logging
|
|
19
|
+
cache_opts = Legion::Settings[:cache].merge(opts)
|
|
20
|
+
cache_opts[:value_max_bytes] ||= 8 * 1024 * 1024
|
|
21
|
+
|
|
17
22
|
@client = ConnectionPool.new(size: pool_size, timeout: timeout) do
|
|
18
|
-
Dalli::Client.new(servers,
|
|
23
|
+
Dalli::Client.new(servers, cache_opts)
|
|
19
24
|
end
|
|
20
25
|
|
|
21
26
|
@connected = true
|
|
@@ -23,19 +28,27 @@ module Legion
|
|
|
23
28
|
end
|
|
24
29
|
|
|
25
30
|
def get(key)
|
|
26
|
-
client.with { |conn| conn.get(key) }
|
|
31
|
+
result = client.with { |conn| conn.get(key) }
|
|
32
|
+
Legion::Logging.debug "[cache] GET #{key} hit=#{!result.nil?}"
|
|
33
|
+
result
|
|
27
34
|
end
|
|
28
35
|
|
|
29
36
|
def fetch(key, ttl = nil)
|
|
30
|
-
client.with { |conn| conn.fetch(key, ttl) }
|
|
37
|
+
result = client.with { |conn| conn.fetch(key, ttl) }
|
|
38
|
+
Legion::Logging.debug "[cache] FETCH #{key} hit=#{!result.nil?}"
|
|
39
|
+
result
|
|
31
40
|
end
|
|
32
41
|
|
|
33
42
|
def set(key, value, ttl = 180)
|
|
34
|
-
client.with { |conn| conn.set(key, value, ttl).positive? }
|
|
43
|
+
result = client.with { |conn| conn.set(key, value, ttl).positive? }
|
|
44
|
+
Legion::Logging.debug "[cache] SET #{key} ttl=#{ttl} success=#{result} value_class=#{value.class}"
|
|
45
|
+
result
|
|
35
46
|
end
|
|
36
47
|
|
|
37
48
|
def delete(key)
|
|
38
|
-
client.with { |conn| conn.delete(key) == true }
|
|
49
|
+
result = client.with { |conn| conn.delete(key) == true }
|
|
50
|
+
Legion::Logging.debug "[cache] DELETE #{key} success=#{result}"
|
|
51
|
+
result
|
|
39
52
|
end
|
|
40
53
|
|
|
41
54
|
def flush(delay = 0)
|
data/lib/legion/cache/pool.rb
CHANGED
data/lib/legion/cache/redis.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'redis'
|
|
2
4
|
require 'legion/cache/pool'
|
|
3
5
|
|
|
@@ -5,7 +7,7 @@ module Legion
|
|
|
5
7
|
module Cache
|
|
6
8
|
module Redis
|
|
7
9
|
include Legion::Cache::Pool
|
|
8
|
-
extend self
|
|
10
|
+
extend self # rubocop:disable Style/ModuleFunction
|
|
9
11
|
|
|
10
12
|
def client(pool_size: 20, timeout: 5, **)
|
|
11
13
|
return @client unless @client.nil?
|
|
@@ -1,30 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require 'legion/settings'
|
|
5
|
+
rescue StandardError
|
|
6
|
+
# empty block
|
|
7
|
+
end
|
|
8
|
+
|
|
1
9
|
module Legion
|
|
2
10
|
module Cache
|
|
3
11
|
module Settings
|
|
4
12
|
Legion::Settings.merge_settings(:cache, default) if Legion::Settings.method_defined? :merge_settings
|
|
13
|
+
Legion::Settings.merge_settings(:cache_local, local) if Legion::Settings.method_defined? :merge_settings
|
|
5
14
|
def self.default
|
|
6
15
|
{
|
|
7
|
-
driver:
|
|
8
|
-
servers:
|
|
9
|
-
connected:
|
|
10
|
-
enabled:
|
|
11
|
-
namespace:
|
|
12
|
-
compress:
|
|
13
|
-
failover:
|
|
16
|
+
driver: driver,
|
|
17
|
+
servers: ['127.0.0.1:11211'],
|
|
18
|
+
connected: false,
|
|
19
|
+
enabled: true,
|
|
20
|
+
namespace: 'legion',
|
|
21
|
+
compress: false,
|
|
22
|
+
failover: true,
|
|
23
|
+
threadsafe: true,
|
|
24
|
+
expires_in: 0,
|
|
25
|
+
cache_nils: false,
|
|
26
|
+
pool_size: 10,
|
|
27
|
+
timeout: 5,
|
|
28
|
+
serializer: Legion::JSON
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.local
|
|
33
|
+
{
|
|
34
|
+
driver: driver,
|
|
35
|
+
servers: ['127.0.0.1:11211'],
|
|
36
|
+
connected: false,
|
|
37
|
+
enabled: true,
|
|
38
|
+
namespace: 'legion_local',
|
|
39
|
+
compress: false,
|
|
40
|
+
failover: true,
|
|
14
41
|
threadsafe: true,
|
|
15
42
|
expires_in: 0,
|
|
16
43
|
cache_nils: false,
|
|
17
|
-
pool_size:
|
|
18
|
-
timeout:
|
|
44
|
+
pool_size: 5,
|
|
45
|
+
timeout: 3,
|
|
19
46
|
serializer: Legion::JSON
|
|
20
47
|
}
|
|
21
48
|
end
|
|
22
49
|
|
|
23
50
|
def self.driver(prefer = 'dalli')
|
|
24
51
|
secondary = prefer == 'dalli' ? 'redis' : 'dalli'
|
|
25
|
-
if Gem::Specification.find_all_by_name(prefer).
|
|
52
|
+
if Gem::Specification.find_all_by_name(prefer).any?
|
|
26
53
|
prefer
|
|
27
|
-
elsif Gem::Specification.find_all_by_name(secondary).
|
|
54
|
+
elsif Gem::Specification.find_all_by_name(secondary).any?
|
|
28
55
|
secondary
|
|
29
56
|
else
|
|
30
57
|
raise NameError('Legion::Cache.driver is nil')
|
data/lib/legion/cache/version.rb
CHANGED
data/lib/legion/cache.rb
CHANGED
|
@@ -1,29 +1,101 @@
|
|
|
1
|
-
|
|
2
|
-
require_relative 'cache/settings'
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
require 'legion/cache/version'
|
|
4
|
+
require 'legion/cache/settings'
|
|
5
|
+
|
|
6
|
+
require 'legion/cache/memcached'
|
|
7
|
+
require 'legion/cache/redis'
|
|
8
|
+
require 'legion/cache/local'
|
|
6
9
|
|
|
7
10
|
module Legion
|
|
8
11
|
module Cache
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
if Legion::Settings[:cache][:driver] == 'redis'
|
|
13
|
+
extend Legion::Cache::Redis
|
|
14
|
+
else
|
|
15
|
+
extend Legion::Cache::Memcached
|
|
16
|
+
end
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
class << self
|
|
19
|
+
def setup(**)
|
|
14
20
|
return Legion::Settings[:cache][:connected] = true if connected?
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@connected = true
|
|
19
|
-
Legion::Settings[:cache][:connected] = true
|
|
22
|
+
setup_local
|
|
23
|
+
setup_shared(**)
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
def shutdown
|
|
23
27
|
Legion::Logging.info 'Shutting down Legion::Cache'
|
|
24
|
-
close
|
|
28
|
+
close unless @using_local
|
|
29
|
+
Legion::Cache::Local.shutdown if Legion::Cache::Local.connected?
|
|
30
|
+
@using_local = false
|
|
31
|
+
@connected = false
|
|
25
32
|
Legion::Settings[:cache][:connected] = false
|
|
26
33
|
end
|
|
34
|
+
|
|
35
|
+
def local
|
|
36
|
+
Legion::Cache::Local
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def using_local?
|
|
40
|
+
@using_local == true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def get(key)
|
|
44
|
+
return Legion::Cache::Local.get(key) if @using_local
|
|
45
|
+
|
|
46
|
+
super
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def set(key, value, ttl = 180)
|
|
50
|
+
return Legion::Cache::Local.set(key, value, ttl) if @using_local
|
|
51
|
+
|
|
52
|
+
super
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def fetch(key, ttl = nil)
|
|
56
|
+
return Legion::Cache::Local.fetch(key, ttl) if @using_local
|
|
57
|
+
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def delete(key)
|
|
62
|
+
return Legion::Cache::Local.delete(key) if @using_local
|
|
63
|
+
|
|
64
|
+
super
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def flush(delay = 0)
|
|
68
|
+
return Legion::Cache::Local.flush(delay) if @using_local
|
|
69
|
+
|
|
70
|
+
super
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def setup_local
|
|
76
|
+
return if Legion::Cache::Local.connected?
|
|
77
|
+
|
|
78
|
+
Legion::Cache::Local.setup
|
|
79
|
+
rescue StandardError => e
|
|
80
|
+
Legion::Logging.warn "Local cache setup failed: #{e.message}" if defined?(Legion::Logging)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def setup_shared(**)
|
|
84
|
+
client(**Legion::Settings[:cache], **)
|
|
85
|
+
@connected = true
|
|
86
|
+
@using_local = false
|
|
87
|
+
Legion::Settings[:cache][:connected] = true
|
|
88
|
+
rescue StandardError => e
|
|
89
|
+
Legion::Logging.warn "Shared cache unavailable (#{e.message}), falling back to Local" if defined?(Legion::Logging)
|
|
90
|
+
if Legion::Cache::Local.connected?
|
|
91
|
+
@using_local = true
|
|
92
|
+
@connected = true
|
|
93
|
+
Legion::Settings[:cache][:connected] = true
|
|
94
|
+
else
|
|
95
|
+
@connected = false
|
|
96
|
+
Legion::Settings[:cache][:connected] = false
|
|
97
|
+
end
|
|
98
|
+
end
|
|
27
99
|
end
|
|
28
100
|
end
|
|
29
101
|
end
|