cache_failover 0.1.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 +7 -0
- data/.idea/.gitignore +6 -0
- data/.idea/CacheFailover.iml +16 -0
- data/.idea/modules.xml +8 -0
- data/.idea/vcs.xml +6 -0
- data/Rakefile +0 -0
- data/cache_failover.gemspec +36 -0
- data/lib/README.md +50 -0
- data/lib/cache_failover/store.rb +259 -0
- data/lib/cache_failover/version.rb +5 -0
- data/lib/cache_failover.rb +6 -0
- metadata +251 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8d5991671822e08f31cb2622b1acfc7e0f916e6666f5cbab87021a066ada840f
|
4
|
+
data.tar.gz: 77e29f1e8151e7ffead1e13edf1d2ab422724f8acfe260dd1fe842cd1a03254e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d4cb4b6a5a2804d1eb6de0450f03070870d3c56c9fb4cd78be4fed51f32485479762ddba8c03ad3dd6a067fddc703627000b8eb1988c22e2328e5de5ee3bbbd9
|
7
|
+
data.tar.gz: 55e91982231ace2529ecc8a47e81be6bbec454d2f695142fea8c2984a346be120a1ecbc3a679c63767d51cb205976cdcc13b12a945266c91b36efa996eb29cd1
|
data/.idea/.gitignore
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<module type="RUBY_MODULE" version="4">
|
3
|
+
<component name="ModuleRunConfigurationManager">
|
4
|
+
<shared />
|
5
|
+
</component>
|
6
|
+
<component name="NewModuleRootManager">
|
7
|
+
<content url="file://$MODULE_DIR$">
|
8
|
+
<sourceFolder url="file://$MODULE_DIR$/features" isTestSource="true" />
|
9
|
+
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
|
10
|
+
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
11
|
+
</content>
|
12
|
+
<orderEntry type="jdk" jdkName="RVM: ruby-3.3.1 [global]" jdkType="RUBY_SDK" />
|
13
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
14
|
+
<orderEntry type="library" scope="PROVIDED" name="rake (v13.1.0, RVM: ruby-3.3.1 [global]) [gem]" level="application" />
|
15
|
+
</component>
|
16
|
+
</module>
|
data/.idea/modules.xml
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<project version="4">
|
3
|
+
<component name="ProjectModuleManager">
|
4
|
+
<modules>
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/CacheFailover.iml" filepath="$PROJECT_DIR$/.idea/CacheFailover.iml" />
|
6
|
+
</modules>
|
7
|
+
</component>
|
8
|
+
</project>
|
data/.idea/vcs.xml
ADDED
data/Rakefile
ADDED
File without changes
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "cache_failover/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "cache_failover"
|
8
|
+
gem.version = CacheFailover::VERSION
|
9
|
+
gem.authors = ["chronicaust"]
|
10
|
+
gem.email = ["jamiep@supportabilit.com"]
|
11
|
+
gem.summary = %q{ Failover Handling Cache Store for Rails w/ Brotli compression support. (Redis[Hiredis]/Memcached[Dalli]/SolidCache[MySQL/PG]) }
|
12
|
+
gem.description = %q{ This gem enabled automatic failover to a secondary caching method when the primary fails. }
|
13
|
+
gem.homepage = "https://github.com/chronicaust/cache_failover"
|
14
|
+
gem.files = `git ls-files`.split("\n")
|
15
|
+
gem.test_files = gem.files.grep(%r{^(spec)/})
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
gem.license = "MIT"
|
18
|
+
gem.add_dependency "activesupport"
|
19
|
+
gem.add_dependency "brotli"
|
20
|
+
gem.add_dependency "rails-brotli-cache"
|
21
|
+
gem.add_dependency "msgpack"
|
22
|
+
gem.add_development_dependency "solid_cache"
|
23
|
+
gem.add_development_dependency "redis"
|
24
|
+
gem.add_development_dependency "hiredis"
|
25
|
+
gem.add_development_dependency "dalli"
|
26
|
+
gem.add_development_dependency "rspec"
|
27
|
+
gem.add_development_dependency "railties"
|
28
|
+
gem.add_development_dependency "activemodel"
|
29
|
+
gem.add_development_dependency "actionpack"
|
30
|
+
gem.add_development_dependency "byebug"
|
31
|
+
gem.add_development_dependency "rufo"
|
32
|
+
|
33
|
+
if gem.respond_to?(:metadata=)
|
34
|
+
gem.metadata = { "rubygems_mfa_required" => "true" }
|
35
|
+
end
|
36
|
+
end
|
data/lib/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Cache Failover Gem [![Gem Version]]
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
`Gemfile`
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem 'cache_failover'
|
9
|
+
```
|
10
|
+
|
11
|
+
You will need at least 2 cache stores for failover capability.
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'solid_cache' # optional, but you will need at least 2 cache stores
|
15
|
+
gem 'redis' # optional, but you will need at least 2 cache stores
|
16
|
+
gem 'hiredis' # optional, only for redis
|
17
|
+
gem 'dalli' # optional, but you will need at least 2 cache stores (WIP)
|
18
|
+
gem 'cache_failover'
|
19
|
+
```
|
20
|
+
|
21
|
+
## Configuration
|
22
|
+
|
23
|
+
Configure your cache_store normally, but use `CacheFailover::Store` with one argument, an array of hashes with the keys `store` and `options` in the order you would like to failover. Example is shown below:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
config.cache_store = CacheFailover::Store.new(
|
27
|
+
[
|
28
|
+
{
|
29
|
+
store: ActiveSupport::Cache::RedisCacheStore.new(
|
30
|
+
url: CONFIG[:REDIS_URL],
|
31
|
+
password: CONFIG[:REDIS_PASSWORD],
|
32
|
+
),
|
33
|
+
options: {}
|
34
|
+
},
|
35
|
+
{
|
36
|
+
store: SolidCache::Store.new(),
|
37
|
+
options: {
|
38
|
+
expiry_method: :job
|
39
|
+
}
|
40
|
+
}
|
41
|
+
]
|
42
|
+
)
|
43
|
+
```
|
44
|
+
|
45
|
+
## WIP
|
46
|
+
- Dalli/Memcached support
|
47
|
+
- Memory Cache Support
|
48
|
+
- File Cache support
|
49
|
+
- More options
|
50
|
+
- Tests
|
@@ -0,0 +1,259 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module CacheFailover
|
3
|
+
class Store < ::ActiveSupport::Cache::Store
|
4
|
+
MARK_BR_COMPRESSED = "\x02".b
|
5
|
+
|
6
|
+
class BrotliCompressor
|
7
|
+
def self.deflate(payload)
|
8
|
+
::Brotli.deflate(payload, quality: 1)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.inflate(payload)
|
12
|
+
::Brotli.inflate(payload)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
DEFAULT_OPTIONS = {
|
17
|
+
timeout: 5,
|
18
|
+
compress: true,
|
19
|
+
cache_db: 'cache'
|
20
|
+
}
|
21
|
+
|
22
|
+
attr_reader :core_store
|
23
|
+
|
24
|
+
def initialize(cache_stores)
|
25
|
+
_core_store = cache_stores.find do |cs|
|
26
|
+
options(cs[:options])
|
27
|
+
Logger.new("log/#{CONFIG[:RAILS_ENV]}.log").info("CacheFailover: caching_up?: #{cs[:store].class.name}")
|
28
|
+
Logger.new("log/#{CONFIG[:RAILS_ENV]}.log").info("CacheFailover: caching_up?: #{options}")
|
29
|
+
Logger.new("log/#{CONFIG[:RAILS_ENV]}.log").info("#{caching_up?(cs[:store], options)}")
|
30
|
+
caching_up?(cs[:store], options)
|
31
|
+
end
|
32
|
+
@core_store = _core_store[:store]
|
33
|
+
end
|
34
|
+
|
35
|
+
def options(init_options = {})
|
36
|
+
return @init_options if defined?(@init_options) && init_options.blank?
|
37
|
+
@init_options = init_options.reverse_merge(DEFAULT_OPTIONS)
|
38
|
+
end
|
39
|
+
|
40
|
+
def fetch(name, init_options = nil, &block)
|
41
|
+
options(init_options)
|
42
|
+
|
43
|
+
if !block_given? && options[:force]
|
44
|
+
raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
|
45
|
+
end
|
46
|
+
|
47
|
+
get_value(
|
48
|
+
@core_store.fetch(expanded_cache_key(name), options.merge(compress: false)) do
|
49
|
+
if block_given?
|
50
|
+
store_value(block.call, options)
|
51
|
+
else
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
end,
|
55
|
+
options
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def write(name, value, init_options = nil)
|
60
|
+
options(init_options)
|
61
|
+
|
62
|
+
payload = store_value(value, options)
|
63
|
+
|
64
|
+
@core_store.write(
|
65
|
+
expanded_cache_key(name),
|
66
|
+
payload,
|
67
|
+
options.merge(compress: false)
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def read(name, init_options = nil)
|
72
|
+
options(init_options)
|
73
|
+
|
74
|
+
payload = @core_store.read(
|
75
|
+
expanded_cache_key(name),
|
76
|
+
options
|
77
|
+
)
|
78
|
+
|
79
|
+
get_value(payload, options)
|
80
|
+
end
|
81
|
+
|
82
|
+
def write_multi(hash, init_options = nil)
|
83
|
+
options(init_options)
|
84
|
+
|
85
|
+
new_hash = hash.map do |key, val|
|
86
|
+
[
|
87
|
+
expanded_cache_key(key),
|
88
|
+
store_value(val, options),
|
89
|
+
]
|
90
|
+
end
|
91
|
+
|
92
|
+
@core_store.write_multi(
|
93
|
+
new_hash,
|
94
|
+
options.merge(compress: false)
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
def read_multi(*names)
|
99
|
+
options = names.extract_options!
|
100
|
+
names = names.map { |name| expanded_cache_key(name) }
|
101
|
+
options(options)
|
102
|
+
|
103
|
+
core_store.read_multi(*names, options).map do |key, val|
|
104
|
+
[key, get_value(val, options)]
|
105
|
+
end.to_h
|
106
|
+
end
|
107
|
+
|
108
|
+
def fetch_multi(*names)
|
109
|
+
options = names.extract_options!
|
110
|
+
expanded_names = names.map { |name| expanded_cache_key(name) }
|
111
|
+
options(options)
|
112
|
+
|
113
|
+
reads = core_store.send(:read_multi_entries, expanded_names, **options)
|
114
|
+
reads.map do |key, val|
|
115
|
+
[key, store_value(val, options)]
|
116
|
+
end.to_h
|
117
|
+
|
118
|
+
writes = {}
|
119
|
+
ordered = names.index_with do |name|
|
120
|
+
reads.fetch(name) { writes[name] = yield(name) }
|
121
|
+
end
|
122
|
+
|
123
|
+
write_multi(writes)
|
124
|
+
ordered
|
125
|
+
end
|
126
|
+
|
127
|
+
def exist?(name, init_options = {})
|
128
|
+
@core_store.exist?(expanded_cache_key(name), init_options)
|
129
|
+
end
|
130
|
+
|
131
|
+
def delete(name, init_options = {})
|
132
|
+
@core_store.delete(expanded_cache_key(name), init_options)
|
133
|
+
end
|
134
|
+
|
135
|
+
def clear(init_options = {})
|
136
|
+
@core_store.clear(**init_options)
|
137
|
+
end
|
138
|
+
|
139
|
+
def increment(name, amount = 1, **init_options)
|
140
|
+
@core_store.increment(expanded_cache_key(name), amount, **init_options)
|
141
|
+
end
|
142
|
+
|
143
|
+
def decrement(name, amount = 1, **init_options)
|
144
|
+
@core_store.decrement(expanded_cache_key(name), amount, **init_options)
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.supports_cache_versioning?
|
148
|
+
true
|
149
|
+
end
|
150
|
+
|
151
|
+
REDIS ||=
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def redis_cnxn(init_options)
|
156
|
+
@redis_cache_client ||=
|
157
|
+
RedisClient.config(
|
158
|
+
url: CONFIG[:REDIS_URL],
|
159
|
+
password: CONFIG[:REDIS_PASSWORD],
|
160
|
+
driver: init_options[:adapter] || :hiredis,
|
161
|
+
timeout: init_options[:timeout] || 1,
|
162
|
+
inherit_socket: true,
|
163
|
+
).new_pool
|
164
|
+
end
|
165
|
+
|
166
|
+
def cache_db_cnxn(init_options)
|
167
|
+
@db_cache_client ||=
|
168
|
+
ActiveRecord::Base.
|
169
|
+
establish_connection(
|
170
|
+
Rails.configuration.database_configuration[Rails.env.to_s][init_options[:cache_db]]
|
171
|
+
)
|
172
|
+
end
|
173
|
+
|
174
|
+
def caching_up?(store, init_options)
|
175
|
+
begin
|
176
|
+
Timeout.timeout((init_options[:timeout] || 1)) do
|
177
|
+
case store.class.name
|
178
|
+
when 'ActiveSupport::Cache::RedisCacheStore'
|
179
|
+
(redis_cnxn(init_options).call('ping') == 'PONG' rescue false)
|
180
|
+
when 'ActiveSupport::Cache::MemCacheStore'
|
181
|
+
when 'SolidCache::Store'
|
182
|
+
cache_db_cnxn(init_options).with_connection { ActiveRecord::Base.connection.select_value('SELECT 1=1') == 1 }
|
183
|
+
when 'ActiveSupport::Cache::MemoryStore'
|
184
|
+
when 'ActiveSupport::Cache::FileStore'
|
185
|
+
when 'ActiveSupport::Cache::NullStore'
|
186
|
+
end
|
187
|
+
end
|
188
|
+
rescue => ex
|
189
|
+
false
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def serialized(value)
|
194
|
+
mpval = (value.try(:to_msgpack) rescue nil)
|
195
|
+
msval = (Marshal.dump(value) rescue nil)
|
196
|
+
if mpval.present? && mpval.bytesize < msval.bytesize
|
197
|
+
mpval
|
198
|
+
else
|
199
|
+
msval
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def unserialized(payload)
|
204
|
+
begin
|
205
|
+
MessagePack.unpack(payload)
|
206
|
+
rescue => ex
|
207
|
+
Marshal.load(payload)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def compressed(value)
|
212
|
+
begin
|
213
|
+
BrotliCompressor.deflate(value)
|
214
|
+
rescue Brotli::Error => ex
|
215
|
+
Rails.logger.info("CacheFailover Error: BrotliCompressor.deflate: #{ex.message}")
|
216
|
+
value
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def uncompressed(payload)
|
221
|
+
begin
|
222
|
+
BrotliCompressor.inflate(payload.byteslice(1..-1))
|
223
|
+
rescue Brotli::Error => ex
|
224
|
+
Rails.logger.info("CacheFailover Error: BrotliCompressor.inflate: #{ex.message}")
|
225
|
+
payload
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def store_value(value, init_options)
|
230
|
+
return value if value.is_a?(Integer)
|
231
|
+
value = serialized(value)
|
232
|
+
if init_options.blank? || init_options[:compress].blank? || !options[:compress] == false
|
233
|
+
value = compressed(value)
|
234
|
+
MARK_BR_COMPRESSED + value
|
235
|
+
else
|
236
|
+
value
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def get_value(payload, init_options)
|
241
|
+
return nil unless payload.present?
|
242
|
+
return payload if payload.is_a?(Integer)
|
243
|
+
payload =
|
244
|
+
if payload.start_with?(MARK_BR_COMPRESSED)
|
245
|
+
uncompressed(payload)
|
246
|
+
else
|
247
|
+
payload
|
248
|
+
end
|
249
|
+
payload = unserialized(payload)
|
250
|
+
payload
|
251
|
+
end
|
252
|
+
|
253
|
+
def expanded_cache_key(name)
|
254
|
+
"#{::ActiveSupport::Cache.expand_cache_key(name)}"
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
metadata
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cache_failover
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- chronicaust
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-10-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: brotli
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rails-brotli-cache
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: msgpack
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: solid_cache
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: redis
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: hiredis
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: dalli
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: railties
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: activemodel
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: actionpack
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: byebug
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: rufo
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ">="
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0'
|
209
|
+
description: " This gem enabled automatic failover to a secondary caching method when
|
210
|
+
the primary fails. "
|
211
|
+
email:
|
212
|
+
- jamiep@supportabilit.com
|
213
|
+
executables: []
|
214
|
+
extensions: []
|
215
|
+
extra_rdoc_files: []
|
216
|
+
files:
|
217
|
+
- ".idea/.gitignore"
|
218
|
+
- ".idea/CacheFailover.iml"
|
219
|
+
- ".idea/modules.xml"
|
220
|
+
- ".idea/vcs.xml"
|
221
|
+
- Rakefile
|
222
|
+
- cache_failover.gemspec
|
223
|
+
- lib/README.md
|
224
|
+
- lib/cache_failover.rb
|
225
|
+
- lib/cache_failover/store.rb
|
226
|
+
- lib/cache_failover/version.rb
|
227
|
+
homepage: https://github.com/chronicaust/cache_failover
|
228
|
+
licenses:
|
229
|
+
- MIT
|
230
|
+
metadata:
|
231
|
+
rubygems_mfa_required: 'true'
|
232
|
+
post_install_message:
|
233
|
+
rdoc_options: []
|
234
|
+
require_paths:
|
235
|
+
- lib
|
236
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
237
|
+
requirements:
|
238
|
+
- - ">="
|
239
|
+
- !ruby/object:Gem::Version
|
240
|
+
version: '0'
|
241
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
242
|
+
requirements:
|
243
|
+
- - ">="
|
244
|
+
- !ruby/object:Gem::Version
|
245
|
+
version: '0'
|
246
|
+
requirements: []
|
247
|
+
rubygems_version: 3.5.11
|
248
|
+
signing_key:
|
249
|
+
specification_version: 4
|
250
|
+
summary: Failover Handling Cache Store for Rails w/ Brotli compression support. (Redis[Hiredis]/Memcached[Dalli]/SolidCache[MySQL/PG])
|
251
|
+
test_files: []
|