cachext 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|