gracefully 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +30 -0
- data/Gemfile +2 -0
- data/README.md +34 -1
- data/lib/gracefully.rb +22 -4
- data/lib/gracefully/all.rb +6 -0
- data/lib/gracefully/circuit_breaker.rb +88 -0
- data/lib/gracefully/command.rb +23 -0
- data/lib/gracefully/command_disabled_error.rb +6 -0
- data/lib/gracefully/consecutive_failures_based_health.rb +75 -0
- data/lib/gracefully/counter.rb +29 -0
- data/lib/gracefully/degradable.rb +36 -0
- data/lib/gracefully/{feature.rb → degradable_command.rb} +4 -3
- data/lib/gracefully/degradable_command_builder.rb +23 -0
- data/lib/gracefully/error.rb +37 -0
- data/lib/gracefully/health.rb +29 -0
- data/lib/gracefully/mutex_based_synchronized_counter.rb +30 -0
- data/lib/gracefully/retried_command.rb +25 -0
- data/lib/gracefully/short_circuited_command.rb +23 -0
- data/lib/gracefully/timed_command.rb +19 -0
- data/lib/gracefully/togglable_command.rb +20 -0
- data/lib/gracefully/try.rb +6 -34
- data/lib/gracefully/version.rb +1 -1
- data/spec/circuit_breaker_spec.rb +142 -0
- data/spec/command_spec.rb +34 -0
- data/spec/consecutive_failures_based_health_spec.rb +78 -0
- data/spec/degradable_spec.rb +49 -0
- data/spec/gracefully_spec.rb +136 -10
- data/spec/mutex_based_synchronized_counter_spec.rb +50 -0
- data/spec/retried_command_spec.rb +93 -0
- data/spec/short_circuited_command_spec.rb +136 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/timecop_helper.rb +3 -0
- data/spec/timed_command_spec.rb +73 -0
- data/spec/togglable_command_spec.rb +31 -0
- metadata +39 -5
- data/lib/gracefully/feature_builder.rb +0 -24
- data/lib/gracefully/health_meter.rb +0 -90
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'gracefully'
|
2
|
+
require 'gracefully/command'
|
3
|
+
require 'gracefully/timed_command'
|
4
|
+
|
5
|
+
RSpec.shared_examples "a timed command" do
|
6
|
+
let(:input1) {
|
7
|
+
'input1'
|
8
|
+
}
|
9
|
+
|
10
|
+
let(:callable) {
|
11
|
+
Gracefully::Command.new(&usually)
|
12
|
+
}
|
13
|
+
|
14
|
+
context 'when the block given to the command succeeds without any error' do
|
15
|
+
let(:usually) {
|
16
|
+
-> arg1 { 'usually:arg1:' + arg1 }
|
17
|
+
}
|
18
|
+
|
19
|
+
it { is_expected.to eq('usually:arg1:input1') }
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when the usually block fails with an error' do
|
23
|
+
let(:usually) {
|
24
|
+
-> arg1 { raise 'simulated error' }
|
25
|
+
}
|
26
|
+
|
27
|
+
specify { expect { subject }.to raise_error('simulated error') }
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when the usually block call takes longer than the time out period' do
|
31
|
+
let(:usually) {
|
32
|
+
-> arg1 { sleep(0.1) }
|
33
|
+
}
|
34
|
+
|
35
|
+
specify { expect { subject }.to raise_error(Timeout::Error) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
RSpec.describe Gracefully::TimedCommand do
|
40
|
+
context "command creation with invalid number of arguments" do
|
41
|
+
subject {
|
42
|
+
described_class.new(1, 2, 3)
|
43
|
+
}
|
44
|
+
|
45
|
+
specify { expect { subject }.to raise_error(/Invalid number of arguments: 3/) }
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "feature call result" do
|
49
|
+
context 'made of a callable object' do
|
50
|
+
subject {
|
51
|
+
described_class.new(usually, timeout: 0.01).call input1
|
52
|
+
}
|
53
|
+
|
54
|
+
it_behaves_like 'a timed command'
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'made of a command' do
|
58
|
+
subject {
|
59
|
+
described_class.new(callable, timeout: 0.01).call input1
|
60
|
+
}
|
61
|
+
|
62
|
+
it_behaves_like 'a timed command'
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'made of a block' do
|
66
|
+
subject {
|
67
|
+
described_class.new(timeout: 0.01, &usually).call input1
|
68
|
+
}
|
69
|
+
|
70
|
+
it_behaves_like 'a timed command'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'gracefully/togglable_command'
|
2
|
+
|
3
|
+
RSpec.describe Gracefully::TogglableCommand do
|
4
|
+
subject {
|
5
|
+
described_class.new(run_only_if: -> { current_user == target_user }) { |a| a + ' modified' }.call arg
|
6
|
+
}
|
7
|
+
|
8
|
+
let(:arg) {
|
9
|
+
'input'
|
10
|
+
}
|
11
|
+
|
12
|
+
let(:current_user) {
|
13
|
+
'mikoto'
|
14
|
+
}
|
15
|
+
|
16
|
+
context 'when the `run_only_if` block returns true' do
|
17
|
+
let(:target_user) {
|
18
|
+
'mikoto'
|
19
|
+
}
|
20
|
+
|
21
|
+
it { is_expected.to eq('input modified') }
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when the `run_only_if` block returns false' do
|
25
|
+
let(:target_user) {
|
26
|
+
'kuroko'
|
27
|
+
}
|
28
|
+
|
29
|
+
specify { expect { subject }.to raise_error(Gracefully::CommandDisabledError) }
|
30
|
+
end
|
31
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gracefully
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yusuke KUOKA
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -47,20 +47,44 @@ extra_rdoc_files: []
|
|
47
47
|
files:
|
48
48
|
- ".gitignore"
|
49
49
|
- ".rspec"
|
50
|
+
- ".travis.yml"
|
50
51
|
- Gemfile
|
51
52
|
- LICENSE.txt
|
52
53
|
- README.md
|
53
54
|
- Rakefile
|
54
55
|
- gracefully.gemspec
|
55
56
|
- lib/gracefully.rb
|
56
|
-
- lib/gracefully/
|
57
|
-
- lib/gracefully/
|
57
|
+
- lib/gracefully/all.rb
|
58
|
+
- lib/gracefully/circuit_breaker.rb
|
59
|
+
- lib/gracefully/command.rb
|
60
|
+
- lib/gracefully/command_disabled_error.rb
|
61
|
+
- lib/gracefully/consecutive_failures_based_health.rb
|
62
|
+
- lib/gracefully/counter.rb
|
63
|
+
- lib/gracefully/degradable.rb
|
64
|
+
- lib/gracefully/degradable_command.rb
|
65
|
+
- lib/gracefully/degradable_command_builder.rb
|
66
|
+
- lib/gracefully/error.rb
|
58
67
|
- lib/gracefully/feature_repository.rb
|
59
|
-
- lib/gracefully/
|
68
|
+
- lib/gracefully/health.rb
|
69
|
+
- lib/gracefully/mutex_based_synchronized_counter.rb
|
70
|
+
- lib/gracefully/retried_command.rb
|
71
|
+
- lib/gracefully/short_circuited_command.rb
|
72
|
+
- lib/gracefully/timed_command.rb
|
73
|
+
- lib/gracefully/togglable_command.rb
|
60
74
|
- lib/gracefully/try.rb
|
61
75
|
- lib/gracefully/version.rb
|
76
|
+
- spec/circuit_breaker_spec.rb
|
77
|
+
- spec/command_spec.rb
|
78
|
+
- spec/consecutive_failures_based_health_spec.rb
|
79
|
+
- spec/degradable_spec.rb
|
62
80
|
- spec/gracefully_spec.rb
|
81
|
+
- spec/mutex_based_synchronized_counter_spec.rb
|
82
|
+
- spec/retried_command_spec.rb
|
83
|
+
- spec/short_circuited_command_spec.rb
|
63
84
|
- spec/spec_helper.rb
|
85
|
+
- spec/timecop_helper.rb
|
86
|
+
- spec/timed_command_spec.rb
|
87
|
+
- spec/togglable_command_spec.rb
|
64
88
|
homepage: https://github.com/crowdworks/gracefully
|
65
89
|
licenses:
|
66
90
|
- MIT
|
@@ -86,5 +110,15 @@ signing_key:
|
|
86
110
|
specification_version: 4
|
87
111
|
summary: ensures features gracefully degrade based on error rate or turnaround time.
|
88
112
|
test_files:
|
113
|
+
- spec/circuit_breaker_spec.rb
|
114
|
+
- spec/command_spec.rb
|
115
|
+
- spec/consecutive_failures_based_health_spec.rb
|
116
|
+
- spec/degradable_spec.rb
|
89
117
|
- spec/gracefully_spec.rb
|
118
|
+
- spec/mutex_based_synchronized_counter_spec.rb
|
119
|
+
- spec/retried_command_spec.rb
|
120
|
+
- spec/short_circuited_command_spec.rb
|
90
121
|
- spec/spec_helper.rb
|
122
|
+
- spec/timecop_helper.rb
|
123
|
+
- spec/timed_command_spec.rb
|
124
|
+
- spec/togglable_command_spec.rb
|
@@ -1,24 +0,0 @@
|
|
1
|
-
module Gracefully
|
2
|
-
class FeatureBuilder
|
3
|
-
def initialize(feature_name)
|
4
|
-
@feature_name = feature_name
|
5
|
-
end
|
6
|
-
|
7
|
-
def usually(&block)
|
8
|
-
@usually = block
|
9
|
-
self
|
10
|
-
end
|
11
|
-
|
12
|
-
def fallback_to(&block)
|
13
|
-
@fallback_to = block
|
14
|
-
|
15
|
-
build
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def build
|
21
|
-
Feature.new(name: @feature_name, usually: @usually, fallback_to: @fallback_to)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
module Gracefully
|
2
|
-
class HealthMeter
|
3
|
-
def initialize(args)
|
4
|
-
@healthy_count = 0
|
5
|
-
@unhealthy_count = 0
|
6
|
-
@state = Healthy.new configuration: args[:configuration]
|
7
|
-
end
|
8
|
-
|
9
|
-
def mark_healthy
|
10
|
-
@state = @state.mark_healthy
|
11
|
-
end
|
12
|
-
|
13
|
-
def mark_unhealthy
|
14
|
-
@state = @state.mark_unhealthy
|
15
|
-
end
|
16
|
-
|
17
|
-
def healthy?
|
18
|
-
@state.healthy?
|
19
|
-
end
|
20
|
-
|
21
|
-
def unhealthy?
|
22
|
-
@state.unhealthy?
|
23
|
-
end
|
24
|
-
|
25
|
-
class State
|
26
|
-
def unhealthy?
|
27
|
-
!healthy?
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class Configuration
|
32
|
-
attr_reader :healthy_threshold, :unhealthy_threshold
|
33
|
-
|
34
|
-
def initialize(args)
|
35
|
-
@healthy_threshold = args[:healthy_threshold]
|
36
|
-
@unhealthy_threshold = args[:unhealthy_threshold]
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class Healthy < State
|
41
|
-
def initialize(args)
|
42
|
-
@unhealthy_count = 0
|
43
|
-
@configuration = args[:configuration]
|
44
|
-
end
|
45
|
-
|
46
|
-
def mark_healthy
|
47
|
-
self
|
48
|
-
end
|
49
|
-
|
50
|
-
def mark_unhealthy
|
51
|
-
@unhealthy_count += 1
|
52
|
-
|
53
|
-
if @unhealthy_count <= @configuration.unhealthy_threshold
|
54
|
-
self
|
55
|
-
else
|
56
|
-
Unhealthy.new configuration: @configuration
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def healthy?
|
61
|
-
true
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
class Unhealthy < State
|
66
|
-
def initialize(args)
|
67
|
-
@healthy_count = 0
|
68
|
-
@configuration = args[:configuration]
|
69
|
-
end
|
70
|
-
|
71
|
-
def mark_healthy
|
72
|
-
@healthy_count += 1
|
73
|
-
|
74
|
-
if @healthy_count <= @configuration.healthy_threshold
|
75
|
-
self
|
76
|
-
else
|
77
|
-
Healthy.new configuration: @configuration
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def mark_unhealthy
|
82
|
-
self
|
83
|
-
end
|
84
|
-
|
85
|
-
def healthy?
|
86
|
-
false
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|