expeditor 0.4.0 → 0.5.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 +0 -1
- data/CHANGELOG.md +9 -0
- data/README.md +23 -1
- data/examples/example.rb +2 -2
- data/expeditor.gemspec +2 -0
- data/lib/expeditor/command.rb +65 -45
- data/lib/expeditor/errors.rb +5 -0
- data/lib/expeditor/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ee9877b9a720af63ec3a4163d680e43d734c4b7
|
4
|
+
data.tar.gz: 8707da47bb651151d0c9a2306dcbe237e165cfe5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7889e85b9081df0c1aecc1039e0b2193224effe67d5169c6c100027714909b4616cb5c913a02d808bdf0e21669cdfd36ea22829dea1eb4bdc09df8dc0a8e95b5
|
7
|
+
data.tar.gz: c36f0c9b174b2043de7da8628afa6379f655d38a57538d99669e5124e24eff5e067ed96ad209e0a25ddc70cdcdaf7171c3de21c9ffbcb99e85ef78c6015b3864
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
<!-- ## Master (unreleased) -->
|
2
|
+
|
3
|
+
## 0.5.0
|
4
|
+
|
5
|
+
- Add a `current_thread` option of `Expeditor::Command#start` method to execute a task on current thread [#13](https://github.com/cookpad/expeditor/pull/13)
|
6
|
+
- Drop support for MRI 2.0.x [#15](https://github.com/cookpad/expeditor/pull/15)
|
7
|
+
- Deprecate Expeditor::Command#with_fallback. Use `set_fallback` instead [#14](https://github.com/cookpad/expeditor/pull/14)
|
8
|
+
- Do not allow set_fallback call after command is started. [#18](https://github.com/cookpad/expeditor/pull/18)
|
9
|
+
|
1
10
|
## 0.4.0
|
2
11
|
- Add Expeditor::Service#current\_status [#9](https://github.com/cookpad/expeditor/issues/9)
|
3
12
|
- Add Expeditor::Service#reset\_status! [#10](https://github.com/cookpad/expeditor/issues/10)
|
data/README.md
CHANGED
@@ -8,6 +8,8 @@ It is inspired by [Netflix/Hystrix](https://github.com/Netflix/Hystrix).
|
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
11
|
+
Expeditor currently supports Ruby 2.1 and higher.
|
12
|
+
|
11
13
|
Add this line to your application's Gemfile:
|
12
14
|
|
13
15
|
```ruby
|
@@ -68,7 +70,7 @@ command = Expeditor::Command.new do
|
|
68
70
|
end
|
69
71
|
|
70
72
|
# use fallback value if command is failed
|
71
|
-
command_with_fallback = command.
|
73
|
+
command_with_fallback = command.set_fallback do |e|
|
72
74
|
log(e)
|
73
75
|
default_value
|
74
76
|
end
|
@@ -145,6 +147,26 @@ command = Expeditor::Command.new(service: service) do
|
|
145
147
|
end
|
146
148
|
```
|
147
149
|
|
150
|
+
### synchronous execution
|
151
|
+
|
152
|
+
Use `current_thread` option of `#start`, command executes synchronous on current thread.
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
command1 = Expeditor::Command.new do
|
156
|
+
...
|
157
|
+
end
|
158
|
+
|
159
|
+
command2 = Expeditor::Command.new do
|
160
|
+
...
|
161
|
+
end
|
162
|
+
|
163
|
+
command1.start(current_thread: true) # blocking
|
164
|
+
command2.start(current_thread: true) # blocking
|
165
|
+
|
166
|
+
command1.get
|
167
|
+
command2.get
|
168
|
+
```
|
169
|
+
|
148
170
|
## Development
|
149
171
|
|
150
172
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/examples/example.rb
CHANGED
@@ -24,7 +24,7 @@ command2 = Expeditor::Command.new(service: service, timeout: 0.5) do
|
|
24
24
|
'command2'
|
25
25
|
end
|
26
26
|
# command2_d is command2 with fallback
|
27
|
-
command2_d = command2.
|
27
|
+
command2_d = command2.set_fallback do |e|
|
28
28
|
'command2 fallback'
|
29
29
|
end
|
30
30
|
|
@@ -44,7 +44,7 @@ command4 = Expeditor::Command.new(
|
|
44
44
|
sleep 0.3
|
45
45
|
v2 + ', ' + v3
|
46
46
|
end
|
47
|
-
command4_d = command4.
|
47
|
+
command4_d = command4.set_fallback do
|
48
48
|
'command4 fallback'
|
49
49
|
end
|
50
50
|
|
data/expeditor.gemspec
CHANGED
@@ -19,6 +19,8 @@ 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.required_ruby_version = '>= 2.1.0'
|
23
|
+
|
22
24
|
spec.add_runtime_dependency "concurrent-ruby", "~> 1.0.0"
|
23
25
|
spec.add_runtime_dependency "concurrent-ruby-ext", "~> 1.0.0"
|
24
26
|
spec.add_runtime_dependency "retryable", "> 1.0"
|
data/lib/expeditor/command.rb
CHANGED
@@ -14,41 +14,49 @@ module Expeditor
|
|
14
14
|
@service = opts.fetch(:service, Expeditor::Services.default)
|
15
15
|
@timeout = opts[:timeout]
|
16
16
|
@dependencies = opts.fetch(:dependencies, [])
|
17
|
-
|
18
|
-
@
|
17
|
+
|
18
|
+
@normal_future = nil
|
19
19
|
@retryable_options = Concurrent::IVar.new
|
20
|
+
@normal_block = block
|
21
|
+
@fallback_block = nil
|
22
|
+
@ivar = Concurrent::IVar.new
|
20
23
|
end
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
+
# @param current_thread [Boolean] Execute the task on current thread(blocking)
|
26
|
+
def start(current_thread: false)
|
27
|
+
unless started?
|
28
|
+
if current_thread
|
29
|
+
prepare(Concurrent::ImmediateExecutor.new)
|
30
|
+
else
|
31
|
+
prepare
|
32
|
+
end
|
25
33
|
@normal_future.safe_execute
|
26
34
|
end
|
27
35
|
self
|
28
36
|
end
|
29
37
|
|
30
38
|
# Equivalent to retryable gem options
|
31
|
-
def start_with_retry(
|
32
|
-
|
39
|
+
def start_with_retry(current_thread: false, **retryable_options)
|
40
|
+
unless started?
|
33
41
|
@retryable_options.set(retryable_options)
|
34
|
-
start
|
42
|
+
start(current_thread: current_thread)
|
35
43
|
end
|
36
44
|
self
|
37
45
|
end
|
38
46
|
|
39
47
|
def started?
|
40
|
-
@normal_future.executed?
|
48
|
+
@normal_future && @normal_future.executed?
|
41
49
|
end
|
42
50
|
|
43
51
|
def get
|
44
|
-
raise NotStartedError
|
52
|
+
raise NotStartedError unless started?
|
45
53
|
@normal_future.get_or_else do
|
46
|
-
if @
|
47
|
-
@
|
48
|
-
if @
|
49
|
-
raise @
|
54
|
+
if @fallback_block && @service.fallback_enabled?
|
55
|
+
@ivar.wait
|
56
|
+
if @ivar.rejected?
|
57
|
+
raise @ivar.reason
|
50
58
|
else
|
51
|
-
@
|
59
|
+
@ivar.value
|
52
60
|
end
|
53
61
|
else
|
54
62
|
raise @normal_future.reason
|
@@ -56,16 +64,22 @@ module Expeditor
|
|
56
64
|
end
|
57
65
|
end
|
58
66
|
|
67
|
+
def set_fallback(&block)
|
68
|
+
if started?
|
69
|
+
raise AlreadyStartedError, "Do not allow set_fallback call after command is started"
|
70
|
+
end
|
71
|
+
reset_fallback(&block)
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
59
75
|
def with_fallback(&block)
|
60
|
-
|
61
|
-
|
62
|
-
command
|
76
|
+
warn 'Expeditor::Command#with_fallback is deprecated. Please use set_fallback instead'
|
77
|
+
set_fallback(&block)
|
63
78
|
end
|
64
79
|
|
65
80
|
def wait
|
66
|
-
raise NotStartedError
|
67
|
-
@
|
68
|
-
@fallback_var.wait if @fallback_var && @service.fallback_enabled?
|
81
|
+
raise NotStartedError unless started?
|
82
|
+
@ivar.wait
|
69
83
|
end
|
70
84
|
|
71
85
|
# command.on_complete do |success, value, reason|
|
@@ -109,25 +123,12 @@ module Expeditor
|
|
109
123
|
Command.new(opts, &block).start
|
110
124
|
end
|
111
125
|
|
112
|
-
|
126
|
+
private
|
113
127
|
|
114
128
|
def reset_fallback(&block)
|
115
|
-
@
|
116
|
-
@normal_future.add_observer do |_, value, reason|
|
117
|
-
if reason != nil
|
118
|
-
future = RichFuture.new(executor: Concurrent.global_io_executor) do
|
119
|
-
success, val, reason = Concurrent::SafeTaskExecutor.new(block, rescue_exception: true).execute(reason)
|
120
|
-
@fallback_var.send(:complete, success, val, reason)
|
121
|
-
end
|
122
|
-
future.safe_execute
|
123
|
-
else
|
124
|
-
@fallback_var.send(:complete, true, value, nil)
|
125
|
-
end
|
126
|
-
end
|
129
|
+
@fallback_block = block
|
127
130
|
end
|
128
131
|
|
129
|
-
private
|
130
|
-
|
131
132
|
def breakable_block(args, &block)
|
132
133
|
if @service.open?
|
133
134
|
raise CircuitBreakError
|
@@ -181,8 +182,8 @@ module Expeditor
|
|
181
182
|
# end
|
182
183
|
# end
|
183
184
|
# end
|
184
|
-
def initial_normal(&block)
|
185
|
-
future = RichFuture.new(executor:
|
185
|
+
def initial_normal(executor, &block)
|
186
|
+
future = RichFuture.new(executor: executor) do
|
186
187
|
args = wait_dependencies
|
187
188
|
timeout_block(args, &block)
|
188
189
|
end
|
@@ -224,18 +225,37 @@ module Expeditor
|
|
224
225
|
end
|
225
226
|
|
226
227
|
def on(&callback)
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
228
|
+
@ivar.add_observer(&callback)
|
229
|
+
end
|
230
|
+
|
231
|
+
# set future
|
232
|
+
# set fallback future as an observer
|
233
|
+
# start dependencies
|
234
|
+
def prepare(executor = @service.executor)
|
235
|
+
@normal_future = initial_normal(executor, &@normal_block)
|
236
|
+
@normal_future.add_observer do |_, value, reason|
|
237
|
+
if reason # failure
|
238
|
+
if @fallback_block
|
239
|
+
future = RichFuture.new(executor: executor) do
|
240
|
+
success, value, reason = Concurrent::SafeTaskExecutor.new(@fallback_block, rescue_exception: true).execute(reason)
|
241
|
+
@ivar.send(:complete, success, value, reason)
|
242
|
+
end
|
243
|
+
future.safe_execute
|
244
|
+
else
|
245
|
+
@ivar.fail(reason)
|
246
|
+
end
|
247
|
+
else # success
|
248
|
+
@ivar.set(value)
|
249
|
+
end
|
231
250
|
end
|
251
|
+
|
252
|
+
@dependencies.each(&:start)
|
232
253
|
end
|
233
254
|
|
234
255
|
class ConstCommand < Command
|
235
256
|
def initialize(value)
|
236
|
-
|
237
|
-
|
238
|
-
@normal_future = RichFuture.new {}.set(value)
|
257
|
+
super(){ value }
|
258
|
+
self.start
|
239
259
|
end
|
240
260
|
end
|
241
261
|
end
|
data/lib/expeditor/errors.rb
CHANGED
@@ -4,10 +4,15 @@ module Expeditor
|
|
4
4
|
NotStartedError = Class.new(StandardError)
|
5
5
|
RejectedExecutionError = Concurrent::RejectedExecutionError
|
6
6
|
CircuitBreakError = Class.new(StandardError)
|
7
|
+
AlreadyStartedError = Class.new(StandardError)
|
7
8
|
class DependencyError < StandardError
|
8
9
|
attr :error
|
9
10
|
def initialize(e)
|
10
11
|
@error = e
|
11
12
|
end
|
13
|
+
|
14
|
+
def message
|
15
|
+
@error.message
|
16
|
+
end
|
12
17
|
end
|
13
18
|
end
|
data/lib/expeditor/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: expeditor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- shohei-yasutake
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -135,7 +135,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
135
135
|
requirements:
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
138
|
+
version: 2.1.0
|
139
139
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
140
|
requirements:
|
141
141
|
- - ">="
|