aws-flow 2.4.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/aws-flow.gemspec +1 -0
- data/lib/aws/decider.rb +0 -1
- data/lib/aws/decider/starter.rb +6 -8
- data/lib/aws/decider/utilities.rb +6 -0
- data/lib/aws/decider/version.rb +1 -1
- data/lib/aws/decider/worker.rb +6 -0
- data/lib/aws/flow/future.rb +86 -6
- data/lib/aws/flow/implementation.rb +84 -13
- data/lib/aws/runner.rb +1 -1
- data/lib/aws/templates.rb +2 -0
- data/lib/aws/templates/activity.rb +41 -0
- data/lib/aws/templates/default.rb +11 -8
- data/lib/aws/templates/result.rb +183 -0
- data/lib/aws/templates/starter.rb +152 -226
- data/lib/aws/templates/utilities.rb +59 -0
- data/spec/aws/decider/integration/activity_spec.rb +1 -0
- data/spec/aws/decider/integration/options_spec.rb +16 -9
- data/spec/aws/decider/integration/starter_spec.rb +6 -7
- data/spec/aws/decider/unit/starter_spec.rb +2 -2
- data/spec/aws/decider/unit/worker_spec.rb +42 -0
- data/spec/aws/flow/{async_backtrace_spec.rb → unit/async_backtrace_spec.rb} +0 -0
- data/spec/aws/flow/{async_scope_spec.rb → unit/async_scope_spec.rb} +0 -0
- data/spec/aws/flow/{begin_rescue_ensure_spec.rb → unit/begin_rescue_ensure_spec.rb} +0 -0
- data/spec/aws/flow/unit/external_condition_variable_spec.rb +59 -0
- data/spec/aws/flow/{external_task_spec.rb → unit/external_task_spec.rb} +0 -0
- data/spec/aws/flow/{factories.rb → unit/factories.rb} +0 -0
- data/spec/aws/flow/{fiber_condition_variable_spec.rb → unit/fiber_condition_variable_spec.rb} +0 -0
- data/spec/aws/flow/{fiber_spec.rb → unit/fiber_spec.rb} +0 -0
- data/spec/aws/flow/{flow_spec.rb → unit/flow_spec.rb} +0 -0
- data/spec/aws/flow/{future_spec.rb → unit/future_spec.rb} +188 -0
- data/spec/aws/flow/{simple_dfa_spec.rb → unit/simple_dfa_spec.rb} +0 -0
- data/spec/aws/runner/integration/runner_integration_spec.rb +1 -0
- data/spec/aws/runner/unit/runner_unit_spec.rb +3 -3
- data/spec/aws/templates/unit/activity_spec.rb +9 -10
- data/spec/aws/templates/unit/base_spec.rb +10 -11
- data/spec/aws/templates/unit/default_spec.rb +23 -6
- data/spec/aws/templates/unit/result_spec.rb +130 -0
- data/spec/aws/templates/unit/starter_spec.rb +32 -105
- data/spec/aws/templates/unit/utilities_spec.rb +80 -0
- metadata +19 -13
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MmMyMmUwNjY1YWUzZWExMDUxNDc2MGM3YTRmYTQzYmEzZWExN2YwMw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZWJhNjZjMDgzNTE0NDgwYzI1NjQ4ZDVmNjA2MjM5YzYzODI2MzA5ZQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MTg4NTYwYmU3YTI1NzAxYzZhZWUyMWZlZGVlNGVmNWE3MGQ2NDQzYzJlYWM3
|
10
|
+
ZmM2MjE0NGNhODhkOWNhYjRkMDM4OGNkMzU5YzU4ZjUzYmE3NmU3NDYxMDRi
|
11
|
+
YTFiYjJmOTZmODUxNzk2NDJjYjczOGNhMGY1NTU2NjkxZDhhYjQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MTAxY2Y4ZWMwYmIyYThiOGE5MGNmODc5NTE3MTM3NDkxYTYxYTNkYTU4MmM1
|
14
|
+
NmI2Njk4NzlhMmI0MTlhZTUzODIzOTZkNTRkMjRlODFhY2FmMDlhZjQ2ZDFm
|
15
|
+
Zjg1NTE3ZWU0NGE4MmNjYmQyNjhhYTZlMDU0MmI3YjBhZWM4YzQ=
|
data/aws-flow.gemspec
CHANGED
@@ -4,6 +4,7 @@ Gem::Specification.new do |s|
|
|
4
4
|
s.name = 'aws-flow'
|
5
5
|
s.version = AWS::Flow::version
|
6
6
|
s.date = Time.now
|
7
|
+
s.license = "Apache-2.0"
|
7
8
|
s.summary = "AWS Flow Framework for Ruby"
|
8
9
|
s.description = "Library to provide the AWS Flow Framework for Ruby"
|
9
10
|
s.authors = "Michael Steger, Paritosh Mohan, Jacques Thomas"
|
data/lib/aws/decider.rb
CHANGED
data/lib/aws/decider/starter.rb
CHANGED
@@ -150,13 +150,11 @@ module AWS
|
|
150
150
|
# @param [Hash] opts
|
151
151
|
# Additional options to configure the workflow or activity execution.
|
152
152
|
#
|
153
|
-
# @option opts [true, false] :
|
154
|
-
# *Optional* This boolean flag can be set to true if the result
|
155
|
-
#
|
156
|
-
#
|
157
|
-
#
|
158
|
-
# *Optional* This sets the timeout value for :wait. Default value is
|
159
|
-
# nil.
|
153
|
+
# @option opts [true, false] :get_result
|
154
|
+
# *Optional* This boolean flag can be set to true if the result future
|
155
|
+
# if required. The future can be waited on by using the
|
156
|
+
# AWS::Flow::wait_for_all, AWS::Flow::wait_for_any methods or by
|
157
|
+
# calling the ExternalFuture#get method. Default value is false.
|
160
158
|
#
|
161
159
|
# @option opts [Hash] :exponential_retry
|
162
160
|
# A hash of {AWS::Flow::ExponentialRetryOptions}. Default value is -
|
@@ -200,7 +198,7 @@ module AWS
|
|
200
198
|
# )
|
201
199
|
#
|
202
200
|
def self.start(name_or_klass, input, options = {})
|
203
|
-
AWS::Flow::Templates.start(name_or_klass, input, options)
|
201
|
+
AWS::Flow::Templates::Starter.start(name_or_klass, input, options)
|
204
202
|
end
|
205
203
|
|
206
204
|
end
|
@@ -17,6 +17,12 @@ require 'tmpdir'
|
|
17
17
|
|
18
18
|
module AWS
|
19
19
|
module Flow
|
20
|
+
|
21
|
+
def self.on_windows?
|
22
|
+
require 'rbconfig'
|
23
|
+
(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/).nil? == false
|
24
|
+
end
|
25
|
+
|
20
26
|
# Utilities for the AWS Flow Framework for Ruby.
|
21
27
|
module Utilities
|
22
28
|
# @api private
|
data/lib/aws/decider/version.rb
CHANGED
data/lib/aws/decider/worker.rb
CHANGED
@@ -290,14 +290,20 @@ module AWS
|
|
290
290
|
if @options
|
291
291
|
@logger = @options.logger || Utilities::LogFactory.make_logger(self)
|
292
292
|
@options.logger ||= @logger
|
293
|
+
# Set the number of execution workers to 0 if it's not already set and
|
294
|
+
# if the platform is Windows
|
295
|
+
@options.execution_workers ||= 0 if AWS::Flow.on_windows?
|
293
296
|
max_workers = @options.execution_workers
|
297
|
+
# If max_workers is set to 0, then turn forking off
|
294
298
|
@options.use_forking = false if (max_workers && max_workers.zero?)
|
295
299
|
end
|
296
300
|
max_workers = 20 if (max_workers.nil?)
|
301
|
+
|
297
302
|
@executor = ForkingExecutor.new(
|
298
303
|
:max_workers => max_workers,
|
299
304
|
:logger => @logger
|
300
305
|
)
|
306
|
+
|
301
307
|
@shutdown_first_time_function = lambda do
|
302
308
|
@executor.shutdown Float::INFINITY
|
303
309
|
Kernel.exit
|
data/lib/aws/flow/future.rb
CHANGED
@@ -34,6 +34,11 @@ module AWS
|
|
34
34
|
#
|
35
35
|
class Future
|
36
36
|
|
37
|
+
def initialize
|
38
|
+
@conditional = FiberConditionVariable.new
|
39
|
+
@set = false
|
40
|
+
end
|
41
|
+
|
37
42
|
# Sets the value of the {Future}, and notifies all of the fibers that
|
38
43
|
# tried to call {#get} when this future wasn't ready.
|
39
44
|
# @api private
|
@@ -41,8 +46,8 @@ module AWS
|
|
41
46
|
raise AlreadySetException if @set
|
42
47
|
@set = true
|
43
48
|
@result = result
|
44
|
-
@conditional.broadcast if @conditional
|
45
49
|
@listeners.each { |b| b.call(self) } if @listeners
|
50
|
+
@conditional.broadcast if @conditional
|
46
51
|
self
|
47
52
|
end
|
48
53
|
|
@@ -62,10 +67,7 @@ module AWS
|
|
62
67
|
# @raise CancellationError
|
63
68
|
# when the task is cancelled.
|
64
69
|
def get
|
65
|
-
until @set
|
66
|
-
@conditional ||= FiberConditionVariable.new
|
67
|
-
@conditional.wait
|
68
|
-
end
|
70
|
+
@conditional.wait until @set
|
69
71
|
@result
|
70
72
|
end
|
71
73
|
|
@@ -79,7 +81,7 @@ module AWS
|
|
79
81
|
#
|
80
82
|
# @api private
|
81
83
|
def unset
|
82
|
-
@set =
|
84
|
+
@set = false
|
83
85
|
@result = nil
|
84
86
|
end
|
85
87
|
|
@@ -135,6 +137,84 @@ module AWS
|
|
135
137
|
self
|
136
138
|
end
|
137
139
|
end
|
140
|
+
|
141
|
+
# Represents the result of an asynchronous computation. Methods are
|
142
|
+
# provided to:
|
143
|
+
#
|
144
|
+
# * retrieve the result of the computation, once it is complete ({ExternalFuture#get}).
|
145
|
+
# * check if the computation is complete ({ExternalFuture#set?})
|
146
|
+
# * execute a block when computation is complete ({ExternalFuture#on_set})
|
147
|
+
#
|
148
|
+
# The result of a Future can only be retrieved when the computation has
|
149
|
+
# completed. {ExternalFuture#get} blocks execution, if necessary, until the
|
150
|
+
# ExternalFuture is ready.
|
151
|
+
#
|
152
|
+
# Unlike {Future}, {ExternalFuture#get} doesn't block Fibers. Instead it
|
153
|
+
# blocks the current thread by waiting on a ruby {ConditionVariable}. The
|
154
|
+
# condition variable is signalled when the future is set, which allows the
|
155
|
+
# thread to continue execution when the result is ready. This lets us use
|
156
|
+
# the future outside of an {AsyncScope}
|
157
|
+
#
|
158
|
+
class ExternalFuture < Future
|
159
|
+
|
160
|
+
def initialize
|
161
|
+
@conditional = ConditionVariable.new
|
162
|
+
@mutex = Mutex.new
|
163
|
+
@set = false
|
164
|
+
end
|
165
|
+
|
166
|
+
# Blocks if future is not set. Returns the result of the future once
|
167
|
+
# {#set} is true.
|
168
|
+
#
|
169
|
+
# @return
|
170
|
+
# The result of the future.
|
171
|
+
#
|
172
|
+
# @raise CancellationError
|
173
|
+
# when the task is cancelled.
|
174
|
+
def get(timeout=nil)
|
175
|
+
@mutex.synchronize do
|
176
|
+
unless @set
|
177
|
+
@conditional.wait(@mutex, timeout)
|
178
|
+
raise Timeout::Error.new unless @set
|
179
|
+
end
|
180
|
+
end
|
181
|
+
@result
|
182
|
+
end
|
183
|
+
|
184
|
+
def method_missing(method, *args, &block)
|
185
|
+
@mutex.synchronize do
|
186
|
+
super(method, *args, &block)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
# Wrapper around a ruby {Mutex} and {ConditionVariable} to avoid
|
193
|
+
# writing the synchronization lines repeatedly.
|
194
|
+
# {ExternalConditionVariable#wait} will block the thread until
|
195
|
+
# {ConditionVariable} @cond is signalled
|
196
|
+
#
|
197
|
+
class ExternalConditionVariable
|
198
|
+
|
199
|
+
attr_reader :mutex, :cond
|
200
|
+
|
201
|
+
def initialize
|
202
|
+
@mutex = Mutex.new
|
203
|
+
@cond = ConditionVariable.new
|
204
|
+
end
|
205
|
+
|
206
|
+
# Block the thread till @cond is signalled
|
207
|
+
def wait(timeout=nil)
|
208
|
+
@mutex.synchronize { @cond.wait(@mutex, timeout) }
|
209
|
+
end
|
210
|
+
|
211
|
+
# Pass all messages to the encapsulated {ConditionVariable}
|
212
|
+
def method_missing(method, *args)
|
213
|
+
@cond.send(method, *args)
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
138
218
|
end
|
139
219
|
end
|
140
220
|
end
|
@@ -124,19 +124,7 @@ module AWS
|
|
124
124
|
# A list of the set futures, in the order of being set.
|
125
125
|
#
|
126
126
|
def wait_for_function(function, *futures)
|
127
|
-
|
128
|
-
futures.flatten!
|
129
|
-
return nil if futures.empty?
|
130
|
-
result = futures.select(&:set?)
|
131
|
-
return futures.find(&:set?)if function.call(result, futures)
|
132
|
-
futures.each do |f|
|
133
|
-
f.on_set do |set_one|
|
134
|
-
result << set_one
|
135
|
-
conditional.broadcast if function.call(result, futures)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
conditional.wait
|
139
|
-
result
|
127
|
+
wait_for_function_helper(nil, function, *futures)
|
140
128
|
end
|
141
129
|
|
142
130
|
# Blocks until *any* of the arguments are set.
|
@@ -162,6 +150,89 @@ module AWS
|
|
162
150
|
def wait_for_all(*futures)
|
163
151
|
wait_for_function(lambda {|result, future_list| result.size == future_list.size}, futures)
|
164
152
|
end
|
153
|
+
|
154
|
+
# Blocks until *any* of the arguments are set.
|
155
|
+
#
|
156
|
+
# @param [Array<Future>] futures
|
157
|
+
# A list of futures to wait for. The function will return when at least one of these is set.
|
158
|
+
#
|
159
|
+
# @param [Integer] timeout
|
160
|
+
# The timeout value after which it will raise a Timeout::Error
|
161
|
+
#
|
162
|
+
# @return [Array<Future>]
|
163
|
+
# A list of the set futures, in the order of being set.
|
164
|
+
#
|
165
|
+
def timed_wait_for_any(timeout, *futures)
|
166
|
+
timed_wait_for_function(timeout, lambda {|result, future_list| result.length >= 1 }, futures)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Blocks until *all* of the arguments are set or until timeout expires
|
170
|
+
#
|
171
|
+
# @param [Array<Future>] futures
|
172
|
+
# A list of futures to wait for. The function will return only when all of them are set.
|
173
|
+
#
|
174
|
+
# @param [Integer] timeout
|
175
|
+
# The timeout value after which it will raise a Timeout::Error
|
176
|
+
#
|
177
|
+
# @return [Array<Future>]
|
178
|
+
# A list of the set futures, in the order of being set.
|
179
|
+
#
|
180
|
+
def timed_wait_for_all(timeout, *futures)
|
181
|
+
timed_wait_for_function(timeout, lambda {|result, future_list| result.size == future_list.size}, futures)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Waits for the passed-in function to complete, setting values for the provided futures when it does.
|
185
|
+
#
|
186
|
+
# @param function
|
187
|
+
# The function to wait for.
|
188
|
+
#
|
189
|
+
# @param [Array<Future>] futures
|
190
|
+
# A list of futures to provide values for when the function completes.
|
191
|
+
#
|
192
|
+
# @param [Integer] timeout
|
193
|
+
# The timeout value after which it will raise a Timeout::Error
|
194
|
+
#
|
195
|
+
# @return [Array<Future>]
|
196
|
+
# A list of the set futures, in the order of being set.
|
197
|
+
#
|
198
|
+
def timed_wait_for_function(timeout, function, *futures)
|
199
|
+
wait_for_function_helper(timeout, function, *futures)
|
200
|
+
end
|
201
|
+
|
202
|
+
# Helper method to refactor away the common implementation of
|
203
|
+
# wait_for_function and timed_wait_for_function.
|
204
|
+
def wait_for_function_helper(timeout, function, *futures)
|
205
|
+
futures.flatten!
|
206
|
+
|
207
|
+
f = futures.select { |x| x.is_a?(ExternalFuture) }
|
208
|
+
|
209
|
+
if f.size > 0 && f.size != futures.size
|
210
|
+
raise ArgumentError, "The futures array must contain either all "\
|
211
|
+
"objects of Future or all objects of ExternalFuture"
|
212
|
+
end
|
213
|
+
|
214
|
+
conditional = f.size == 0 ? FiberConditionVariable.new :
|
215
|
+
ExternalConditionVariable.new
|
216
|
+
|
217
|
+
return nil if futures.empty?
|
218
|
+
result = futures.select(&:set?)
|
219
|
+
return futures.find(&:set?)if function.call(result, futures)
|
220
|
+
futures.each do |f|
|
221
|
+
f.on_set do |set_one|
|
222
|
+
result << set_one
|
223
|
+
conditional.broadcast if function.call(result, futures)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
if conditional.is_a?(FiberConditionVariable)
|
228
|
+
conditional.wait
|
229
|
+
else
|
230
|
+
conditional.wait(timeout)
|
231
|
+
raise Timeout::Error.new unless function.call(result, futures)
|
232
|
+
end
|
233
|
+
result
|
234
|
+
end
|
235
|
+
|
165
236
|
end
|
166
237
|
end
|
167
238
|
end
|
data/lib/aws/runner.rb
CHANGED
@@ -240,7 +240,7 @@ module AWS
|
|
240
240
|
|
241
241
|
if json_config['default_workers']
|
242
242
|
# Also register the default result activity type in the given domain
|
243
|
-
AWS::Flow::Templates.register_default_result_activity(domain)
|
243
|
+
AWS::Flow::Templates::Utils.register_default_result_activity(domain)
|
244
244
|
|
245
245
|
klass = AWS::Flow::Templates.default_workflow
|
246
246
|
task_list = FlowConstants.defaults[:task_list]
|
data/lib/aws/templates.rb
CHANGED
@@ -64,6 +64,47 @@ module AWS
|
|
64
64
|
ActivityTemplate.new(name, opts)
|
65
65
|
end
|
66
66
|
|
67
|
+
# This template represents a Result Activity in SWF.
|
68
|
+
class ResultActivityTemplate < ActivityTemplate
|
69
|
+
attr_reader :key
|
70
|
+
|
71
|
+
def initialize(key, opts = {})
|
72
|
+
@key = key
|
73
|
+
|
74
|
+
# Get the name of the result activity
|
75
|
+
name = "#{FlowConstants.defaults[:result_activity_prefix]}."\
|
76
|
+
"#{FlowConstants.defaults[:result_activity_method]}"
|
77
|
+
|
78
|
+
super(name, opts)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Wraps the input into a result hash and calls the ActivityTemplate#run
|
82
|
+
# method to report the result
|
83
|
+
def run(input, context)
|
84
|
+
result = {}
|
85
|
+
result[:key] = @key
|
86
|
+
result[:result] = input
|
87
|
+
super(result, context)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Initializes a result activity template
|
92
|
+
# @param {String} key
|
93
|
+
# A unique key that identifies the result of an activity execution
|
94
|
+
# @param {Hash} options
|
95
|
+
def result(key, opts = {})
|
96
|
+
AWS::Flow::Templates.send(:result, key, opts)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Initializes a result activity template
|
100
|
+
# @param {String} key
|
101
|
+
# A unique key that identifies the result of an activity execution
|
102
|
+
# @param {Hash} options
|
103
|
+
def self.result(key, opts = {})
|
104
|
+
ResultActivityTemplate.new(key, opts)
|
105
|
+
end
|
106
|
+
|
107
|
+
|
67
108
|
end
|
68
109
|
end
|
69
110
|
end
|
@@ -104,8 +104,6 @@ module AWS
|
|
104
104
|
class FlowDefaultResultActivityRuby
|
105
105
|
extend AWS::Flow::Activities
|
106
106
|
|
107
|
-
attr_reader :result
|
108
|
-
|
109
107
|
# Create the activity type with default options
|
110
108
|
activity FlowConstants.defaults[:result_activity_method] do
|
111
109
|
{
|
@@ -116,15 +114,20 @@ module AWS
|
|
116
114
|
}
|
117
115
|
end
|
118
116
|
|
119
|
-
#
|
120
|
-
|
121
|
-
|
117
|
+
# @param writer IO
|
118
|
+
# An optional IO file descripter to write the result to.
|
119
|
+
#
|
120
|
+
def initialize(writer=nil)
|
121
|
+
@writer = writer
|
122
122
|
end
|
123
123
|
|
124
|
-
#
|
124
|
+
# Serialize the input and write it to an IO writer if provided
|
125
125
|
def run(input)
|
126
|
-
|
127
|
-
|
126
|
+
unless input.is_a?(Hash) && input.include?(:key) && input.include?(:result)
|
127
|
+
raise ArgumentError, "Incorrect input format for "\
|
128
|
+
"FlowDefaultResultActivityRuby.run"
|
129
|
+
end
|
130
|
+
@writer.puts Marshal.dump(input) if @writer
|
128
131
|
end
|
129
132
|
|
130
133
|
end
|