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.
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 = 'legion-cache'
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 = 'Legion::Cache integration'
12
- spec.description = 'Used to connect Legion with a cache service like memcached'
13
- spec.homepage = 'https://bitbucket.org/legion-io/legion-cache'
14
- spec.license = 'MIT'
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.add_development_dependency 'bundler', '>= 2'
29
- spec.add_development_dependency 'legion-logging'
30
- spec.add_development_dependency 'legion-settings'
31
- spec.add_development_dependency 'rake'
32
- spec.add_development_dependency 'rspec'
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, Legion::Settings[:cache].merge(opts))
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)
@@ -1,8 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'connection_pool'
2
4
 
3
5
  module Legion
4
6
  module Cache
5
7
  module Pool
8
+ extend self # rubocop:disable Style/ModuleFunction
9
+
6
10
  def connected?
7
11
  @connected ||= false
8
12
  end
@@ -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: driver,
8
- servers: ['127.0.0.1:11211'],
9
- connected: false,
10
- enabled: true,
11
- namespace: 'legion',
12
- compress: false,
13
- failover: true,
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: 10,
18
- timeout: 5,
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).count.positive?
52
+ if Gem::Specification.find_all_by_name(prefer).any?
26
53
  prefer
27
- elsif Gem::Specification.find_all_by_name(secondary).count.positive?
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')
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module Cache
5
- VERSION = '1.1.1'
5
+ VERSION = '1.3.0'
6
6
  end
7
7
  end
data/lib/legion/cache.rb CHANGED
@@ -1,29 +1,101 @@
1
- require_relative 'cache/version'
2
- require_relative 'cache/settings'
1
+ # frozen_string_literal: true
3
2
 
4
- require_relative 'cache/memcached'
5
- require_relative 'cache/redis'
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
- class << self
10
- include Legion::Cache::Memcached if Legion::Settings[:cache][:driver] == 'dalli'
11
- include Legion::Cache::Redis if Legion::Settings[:cache][:driver] == 'redis'
12
+ if Legion::Settings[:cache][:driver] == 'redis'
13
+ extend Legion::Cache::Redis
14
+ else
15
+ extend Legion::Cache::Memcached
16
+ end
12
17
 
13
- def setup(**opts)
18
+ class << self
19
+ def setup(**)
14
20
  return Legion::Settings[:cache][:connected] = true if connected?
15
21
 
16
- return unless client(**Legion::Settings[:cache], **opts)
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