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.
Files changed (41) hide show
  1. checksums.yaml +8 -8
  2. data/aws-flow.gemspec +1 -0
  3. data/lib/aws/decider.rb +0 -1
  4. data/lib/aws/decider/starter.rb +6 -8
  5. data/lib/aws/decider/utilities.rb +6 -0
  6. data/lib/aws/decider/version.rb +1 -1
  7. data/lib/aws/decider/worker.rb +6 -0
  8. data/lib/aws/flow/future.rb +86 -6
  9. data/lib/aws/flow/implementation.rb +84 -13
  10. data/lib/aws/runner.rb +1 -1
  11. data/lib/aws/templates.rb +2 -0
  12. data/lib/aws/templates/activity.rb +41 -0
  13. data/lib/aws/templates/default.rb +11 -8
  14. data/lib/aws/templates/result.rb +183 -0
  15. data/lib/aws/templates/starter.rb +152 -226
  16. data/lib/aws/templates/utilities.rb +59 -0
  17. data/spec/aws/decider/integration/activity_spec.rb +1 -0
  18. data/spec/aws/decider/integration/options_spec.rb +16 -9
  19. data/spec/aws/decider/integration/starter_spec.rb +6 -7
  20. data/spec/aws/decider/unit/starter_spec.rb +2 -2
  21. data/spec/aws/decider/unit/worker_spec.rb +42 -0
  22. data/spec/aws/flow/{async_backtrace_spec.rb → unit/async_backtrace_spec.rb} +0 -0
  23. data/spec/aws/flow/{async_scope_spec.rb → unit/async_scope_spec.rb} +0 -0
  24. data/spec/aws/flow/{begin_rescue_ensure_spec.rb → unit/begin_rescue_ensure_spec.rb} +0 -0
  25. data/spec/aws/flow/unit/external_condition_variable_spec.rb +59 -0
  26. data/spec/aws/flow/{external_task_spec.rb → unit/external_task_spec.rb} +0 -0
  27. data/spec/aws/flow/{factories.rb → unit/factories.rb} +0 -0
  28. data/spec/aws/flow/{fiber_condition_variable_spec.rb → unit/fiber_condition_variable_spec.rb} +0 -0
  29. data/spec/aws/flow/{fiber_spec.rb → unit/fiber_spec.rb} +0 -0
  30. data/spec/aws/flow/{flow_spec.rb → unit/flow_spec.rb} +0 -0
  31. data/spec/aws/flow/{future_spec.rb → unit/future_spec.rb} +188 -0
  32. data/spec/aws/flow/{simple_dfa_spec.rb → unit/simple_dfa_spec.rb} +0 -0
  33. data/spec/aws/runner/integration/runner_integration_spec.rb +1 -0
  34. data/spec/aws/runner/unit/runner_unit_spec.rb +3 -3
  35. data/spec/aws/templates/unit/activity_spec.rb +9 -10
  36. data/spec/aws/templates/unit/base_spec.rb +10 -11
  37. data/spec/aws/templates/unit/default_spec.rb +23 -6
  38. data/spec/aws/templates/unit/result_spec.rb +130 -0
  39. data/spec/aws/templates/unit/starter_spec.rb +32 -105
  40. data/spec/aws/templates/unit/utilities_spec.rb +80 -0
  41. metadata +19 -13
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MDQyOWZlMGM0YjZiZDcwMmQyNjhjNGFhODY3NzE1ZjZhZDhiNmE4YQ==
4
+ MmMyMmUwNjY1YWUzZWExMDUxNDc2MGM3YTRmYTQzYmEzZWExN2YwMw==
5
5
  data.tar.gz: !binary |-
6
- ZTUxM2MwZTJlNzMzMmMxOWY5NTM0MGM2Y2UyMjA2MjE5NWQxMmJkOA==
6
+ ZWJhNjZjMDgzNTE0NDgwYzI1NjQ4ZDVmNjA2MjM5YzYzODI2MzA5ZQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MTJiNjFiODFlY2IyN2Q5ODc2YTViNjY4MTM0NGIwZTc0MzE1Y2FmYzM5NjUw
10
- MDM5YzkyZTFkNDYzOWFmMDA4YjVmNmY5ODM3MGVkMzQwYjMyMmNiNTlmNmU0
11
- NzYyYWNjNmUwYTJkNDg4OTU4ZDVkMjE5MWUxZjA0MDFkZmZiZTM=
9
+ MTg4NTYwYmU3YTI1NzAxYzZhZWUyMWZlZGVlNGVmNWE3MGQ2NDQzYzJlYWM3
10
+ ZmM2MjE0NGNhODhkOWNhYjRkMDM4OGNkMzU5YzU4ZjUzYmE3NmU3NDYxMDRi
11
+ YTFiYjJmOTZmODUxNzk2NDJjYjczOGNhMGY1NTU2NjkxZDhhYjQ=
12
12
  data.tar.gz: !binary |-
13
- ZWY5NmZjZTE2OTg3NjdiMzg5MjBmNjk3OGUyODY5ODQ5NGJlMjc1OGZkMjI5
14
- YTA2NjM3MTA4MjFkMWNmZTVmODVjZTQ2ODZmZDFlMGM5Njk2MGNiMzAxMDc3
15
- M2EwOGNmODg2OWQzZjAwM2EyNjhjYTAxYjQyMjJmYjI0OGI1YzE=
13
+ MTAxY2Y4ZWMwYmIyYThiOGE5MGNmODc5NTE3MTM3NDkxYTYxYTNkYTU4MmM1
14
+ NmI2Njk4NzlhMmI0MTlhZTUzODIzOTZkNTRkMjRlODFhY2FmMDlhZjQ2ZDFm
15
+ Zjg1NTE3ZWU0NGE4MmNjYmQyNjhhYTZlMDU0MmI3YjBhZWM4YzQ=
@@ -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"
@@ -15,7 +15,6 @@
15
15
 
16
16
  require 'aws/flow'
17
17
  include AWS::Flow::Core
18
-
19
18
  require 'aws-sdk-v1'
20
19
  require 'securerandom'
21
20
 
@@ -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] :wait
154
- # *Optional* This boolean flag can be set to true if the result of the
155
- # task is required. Default value is false.
156
- #
157
- # @option opts [Integer] :wait_timeout
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
@@ -16,7 +16,7 @@
16
16
  module AWS
17
17
  module Flow
18
18
  def self.version
19
- "2.4.0"
19
+ "3.0.0"
20
20
  end
21
21
  end
22
22
  end
@@ -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
@@ -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 = nil
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
- conditional = FiberConditionVariable.new
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
@@ -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]
@@ -1,4 +1,6 @@
1
1
  require 'aws/templates/base'
2
2
  require 'aws/templates/activity'
3
3
  require 'aws/templates/default'
4
+ require 'aws/templates/result'
5
+ require 'aws/templates/utilities'
4
6
  require 'aws/templates/starter'
@@ -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
- # Initialize the future upon instantiation
120
- def initialize
121
- @result = Future.new
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
- # Set the future when the activity is run
124
+ # Serialize the input and write it to an IO writer if provided
125
125
  def run(input)
126
- @result.set(input)
127
- input
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