gracefully 0.0.1 → 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 +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
|