cachext 0.3.2 → 0.4.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/cachext.gemspec +2 -1
- data/lib/cachext.rb +4 -1
- data/lib/cachext/breaker.rb +90 -0
- data/lib/cachext/client.rb +2 -1
- data/lib/cachext/configuration.rb +4 -1
- data/lib/cachext/features.rb +1 -0
- data/lib/cachext/features/circuit_breaker.rb +27 -0
- data/lib/cachext/features/lock.rb +2 -2
- data/lib/cachext/options.rb +7 -1
- data/lib/cachext/version.rb +1 -1
- metadata +22 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: efbd13ef3c2d5d01d835978b0344adfb772a01f1
|
4
|
+
data.tar.gz: a134b132ea3aa34818e3063374ae6f3c2f608db5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9879eebaa0620bc3719e97f96ec2eb2cd4b5ee56c7673f56b906e8c5d4657e3dd15e0f54051621e831e1d57706d36b3ec24ec7c786cc5718b1553d8421ce6c23
|
7
|
+
data.tar.gz: 27a8916181d7f7e10e2fae4eda9e3372be97d9fa854cda833cee8d50a9d14aee7fbcc259e1e9f75ad634f46e8e2eb3f489c4cfa04401cdab881fd853bdf59d11
|
data/cachext.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_dependency "activesupport"
|
22
|
+
spec.add_dependency "activesupport", "~> 4.2"
|
23
23
|
spec.add_dependency "redis"
|
24
24
|
spec.add_dependency "redis-namespace"
|
25
25
|
spec.add_dependency "redlock"
|
@@ -31,4 +31,5 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.add_development_dependency "rspec"
|
32
32
|
spec.add_development_dependency "pry"
|
33
33
|
spec.add_development_dependency "thread"
|
34
|
+
spec.add_development_dependency "timecop"
|
34
35
|
end
|
data/lib/cachext.rb
CHANGED
@@ -2,6 +2,7 @@ require "cachext/version"
|
|
2
2
|
require "faraday/error"
|
3
3
|
|
4
4
|
module Cachext
|
5
|
+
autoload :Breaker, "cachext/breaker"
|
5
6
|
autoload :Client, "cachext/client"
|
6
7
|
autoload :Configuration, "cachext/configuration"
|
7
8
|
autoload :Features, "cachext/features"
|
@@ -40,7 +41,8 @@ module Cachext
|
|
40
41
|
|
41
42
|
def self.flush
|
42
43
|
config.cache.clear
|
43
|
-
config.redis.
|
44
|
+
keys = config.redis.keys("cachext:*")
|
45
|
+
config.redis.del(*keys) if keys.length > 0
|
44
46
|
end
|
45
47
|
|
46
48
|
def self.multi klass, ids, options = {}, &block
|
@@ -49,5 +51,6 @@ module Cachext
|
|
49
51
|
|
50
52
|
def self.configure &block
|
51
53
|
@config = Configuration.setup(&block)
|
54
|
+
@client = Client.new @config
|
52
55
|
end
|
53
56
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require "thread"
|
2
|
+
|
3
|
+
module Cachext
|
4
|
+
class Breaker
|
5
|
+
attr_reader :config
|
6
|
+
|
7
|
+
def initialize(config)
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def for(key)
|
12
|
+
WithKey.new(config, key)
|
13
|
+
end
|
14
|
+
|
15
|
+
class WithKey
|
16
|
+
attr_reader :config, :key
|
17
|
+
|
18
|
+
def initialize(config, key)
|
19
|
+
@config = config
|
20
|
+
@key = key
|
21
|
+
end
|
22
|
+
|
23
|
+
def increment_failure
|
24
|
+
redis.pipelined do
|
25
|
+
redis.set key_str(:last_failure), Time.now.to_f
|
26
|
+
redis.incr key_str(:monitor)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def check_health
|
31
|
+
if half_open?
|
32
|
+
if health_check >= config.failure_threshold
|
33
|
+
reset!
|
34
|
+
else
|
35
|
+
increment_health_check
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def reset!
|
41
|
+
redis.del key_str(:monitor),
|
42
|
+
key_str(:health_check),
|
43
|
+
key_str(:last_failure)
|
44
|
+
end
|
45
|
+
|
46
|
+
def open?
|
47
|
+
state == :open
|
48
|
+
end
|
49
|
+
|
50
|
+
def half_open?
|
51
|
+
state == :half_open
|
52
|
+
end
|
53
|
+
|
54
|
+
def state
|
55
|
+
if (lf = last_failure) && (lf + config.breaker_timeout < Time.now.to_f)
|
56
|
+
:half_open
|
57
|
+
elsif monitor >= config.failure_threshold
|
58
|
+
:open
|
59
|
+
else
|
60
|
+
:close
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def monitor
|
65
|
+
redis.get(key_str(:monitor)).to_i
|
66
|
+
end
|
67
|
+
|
68
|
+
def last_failure
|
69
|
+
lf = redis.get key_str(:last_failure)
|
70
|
+
lf.nil? ? nil : lf.to_f
|
71
|
+
end
|
72
|
+
|
73
|
+
def health_check
|
74
|
+
redis.get(key_str(:health_check)).to_i
|
75
|
+
end
|
76
|
+
|
77
|
+
def increment_health_check
|
78
|
+
redis.incr key_str(:health_check)
|
79
|
+
end
|
80
|
+
|
81
|
+
def key_str(name)
|
82
|
+
"#{name}:#{key.raw.map(&:to_s).join(":")}"
|
83
|
+
end
|
84
|
+
|
85
|
+
def redis
|
86
|
+
config.lock_redis
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/cachext/client.rb
CHANGED
@@ -5,6 +5,7 @@ module Cachext
|
|
5
5
|
prepend Features::DebugLogging
|
6
6
|
prepend Features::Lock
|
7
7
|
prepend Features::Backup
|
8
|
+
prepend Features::CircuitBreaker
|
8
9
|
prepend Features::Default
|
9
10
|
|
10
11
|
def initialize config
|
@@ -45,7 +46,7 @@ module Cachext
|
|
45
46
|
end
|
46
47
|
|
47
48
|
def read key, options
|
48
|
-
key.read
|
49
|
+
key.read if options.cache?
|
49
50
|
end
|
50
51
|
|
51
52
|
def write key, fresh, options
|
@@ -13,7 +13,9 @@ module Cachext
|
|
13
13
|
:not_found_errors, # array of errors where we delete the backup and reraise
|
14
14
|
:max_lock_wait, # time in seconds to wait for a lock
|
15
15
|
:debug, # output debug messages to STDERR
|
16
|
-
:heartbeat_expires
|
16
|
+
:heartbeat_expires, # time in seconds for process heardbeat to expire
|
17
|
+
:failure_threshold, # Number of tries before tripping circuit breaker
|
18
|
+
:breaker_timeout # time in seconds to wait before switching breaker to half-open
|
17
19
|
|
18
20
|
MissingConfiguration = Class.new(StandardError)
|
19
21
|
|
@@ -47,6 +49,7 @@ module Cachext
|
|
47
49
|
self.max_lock_wait = 5
|
48
50
|
self.debug = ENV['CACHEXT_DEBUG'] == "true"
|
49
51
|
self.heartbeat_expires = 2
|
52
|
+
self.failure_threshold = 3
|
50
53
|
@debug_mutex = Mutex.new
|
51
54
|
end
|
52
55
|
|
data/lib/cachext/features.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Cachext
|
2
2
|
module Features
|
3
3
|
autoload :Backup, "cachext/features/backup"
|
4
|
+
autoload :CircuitBreaker, "cachext/features/circuit_breaker"
|
4
5
|
autoload :DebugLogging, "cachext/features/debug_logging"
|
5
6
|
autoload :Default, "cachext/features/default"
|
6
7
|
autoload :Lock, "cachext/features/lock"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Cachext
|
2
|
+
module Features
|
3
|
+
module CircuitBreaker
|
4
|
+
attr_reader :breaker
|
5
|
+
|
6
|
+
def initialize config
|
7
|
+
super
|
8
|
+
@breaker = Breaker.new(config)
|
9
|
+
end
|
10
|
+
|
11
|
+
def read key, options
|
12
|
+
circuit = breaker.for(key)
|
13
|
+
if circuit.open?
|
14
|
+
key.read_backup
|
15
|
+
else
|
16
|
+
circuit.check_health
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def handle_error key, options, error
|
22
|
+
breaker.for(key).increment_failure
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -39,7 +39,7 @@ module Cachext
|
|
39
39
|
|
40
40
|
block.call
|
41
41
|
ensure
|
42
|
-
@config.lock_manager.unlock @lock_info
|
42
|
+
@config.lock_manager.unlock @lock_info if @lock_info
|
43
43
|
done = true
|
44
44
|
end
|
45
45
|
|
@@ -54,7 +54,7 @@ module Cachext
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def wait_for_lock key, start_time
|
57
|
-
sleep rand
|
57
|
+
sleep rand(0..(@config.max_lock_wait / 2))
|
58
58
|
if Time.now - start_time > @config.max_lock_wait
|
59
59
|
raise TimeoutWaitingForLock
|
60
60
|
end
|
data/lib/cachext/options.rb
CHANGED
@@ -14,7 +14,8 @@ module Cachext
|
|
14
14
|
errors: config.default_errors,
|
15
15
|
reraise_errors: true,
|
16
16
|
not_found_error: config.not_found_errors,
|
17
|
-
heartbeat_expires: config.heartbeat_expires
|
17
|
+
heartbeat_expires: config.heartbeat_expires,
|
18
|
+
cache: true
|
18
19
|
|
19
20
|
@expires_in = expires_in
|
20
21
|
@default = default
|
@@ -22,6 +23,11 @@ module Cachext
|
|
22
23
|
@reraise_errors = reraise_errors
|
23
24
|
@not_found_error = not_found_error
|
24
25
|
@heartbeat_expires = heartbeat_expires
|
26
|
+
@cache = cache
|
27
|
+
end
|
28
|
+
|
29
|
+
def cache?
|
30
|
+
@cache
|
25
31
|
end
|
26
32
|
end
|
27
33
|
end
|
data/lib/cachext/version.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cachext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Donald Plummer
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '4.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '4.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: redis
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: timecop
|
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'
|
167
181
|
description: Don't calculate the cached value twice at the same time. Use a backup
|
168
182
|
of the data if the service is down.
|
169
183
|
email:
|
@@ -183,10 +197,12 @@ files:
|
|
183
197
|
- bin/setup
|
184
198
|
- cachext.gemspec
|
185
199
|
- lib/cachext.rb
|
200
|
+
- lib/cachext/breaker.rb
|
186
201
|
- lib/cachext/client.rb
|
187
202
|
- lib/cachext/configuration.rb
|
188
203
|
- lib/cachext/features.rb
|
189
204
|
- lib/cachext/features/backup.rb
|
205
|
+
- lib/cachext/features/circuit_breaker.rb
|
190
206
|
- lib/cachext/features/debug_logging.rb
|
191
207
|
- lib/cachext/features/default.rb
|
192
208
|
- lib/cachext/features/lock.rb
|