aws-flow 2.4.0 → 3.0.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 +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
|