aws-flow 1.0.8 → 1.0.9

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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +1 -1
  3. data/Rakefile +18 -31
  4. data/aws-flow.gemspec +1 -1
  5. data/lib/aws/decider.rb +1 -2
  6. data/lib/aws/decider/activity.rb +99 -53
  7. data/lib/aws/decider/activity_definition.rb +43 -7
  8. data/lib/aws/decider/async_decider.rb +56 -57
  9. data/lib/aws/decider/async_retrying_executor.rb +4 -5
  10. data/lib/aws/decider/data_converter.rb +2 -2
  11. data/lib/aws/decider/decider.rb +46 -41
  12. data/lib/aws/decider/decision_context.rb +2 -2
  13. data/lib/aws/decider/exceptions.rb +6 -6
  14. data/lib/aws/decider/executor.rb +15 -11
  15. data/lib/aws/decider/flow_defaults.rb +54 -22
  16. data/lib/aws/decider/generic_client.rb +7 -7
  17. data/lib/aws/decider/history_helper.rb +0 -0
  18. data/lib/aws/decider/implementation.rb +5 -5
  19. data/lib/aws/decider/options.rb +285 -155
  20. data/lib/aws/decider/state_machines.rb +10 -10
  21. data/lib/aws/decider/task_handler.rb +5 -5
  22. data/lib/aws/decider/task_poller.rb +152 -15
  23. data/lib/aws/decider/utilities.rb +14 -14
  24. data/lib/aws/decider/version.rb +1 -1
  25. data/lib/aws/decider/worker.rb +21 -20
  26. data/lib/aws/decider/workflow_client.rb +78 -31
  27. data/lib/aws/decider/workflow_clock.rb +1 -1
  28. data/lib/aws/decider/workflow_definition.rb +5 -5
  29. data/lib/aws/decider/workflow_definition_factory.rb +1 -1
  30. data/lib/aws/decider/workflow_enabled.rb +1 -1
  31. data/lib/aws/flow/async_backtrace.rb +19 -18
  32. data/lib/aws/flow/async_scope.rb +32 -16
  33. data/lib/aws/flow/begin_rescue_ensure.rb +61 -56
  34. data/lib/aws/flow/fiber.rb +14 -6
  35. data/lib/aws/flow/flow_utils.rb +9 -6
  36. data/lib/aws/flow/future.rb +43 -18
  37. data/lib/aws/flow/implementation.rb +34 -18
  38. data/lib/aws/flow/simple_dfa.rb +12 -4
  39. data/lib/aws/flow/tasks.rb +120 -86
  40. data/{test/aws → spec/aws/integration}/integration_spec.rb +0 -0
  41. data/{test/aws → spec/aws/unit}/async_backtrace_spec.rb +1 -0
  42. data/{test/aws → spec/aws/unit}/async_scope_spec.rb +0 -0
  43. data/{test/aws → spec/aws/unit}/begin_rescue_ensure_spec.rb +90 -2
  44. data/{test/aws → spec/aws/unit}/decider_spec.rb +41 -53
  45. data/{test/aws → spec/aws/unit}/external_task_spec.rb +0 -0
  46. data/{test/aws → spec/aws/unit}/factories.rb +0 -0
  47. data/{test/aws → spec/aws/unit}/fiber_condition_variable_spec.rb +0 -0
  48. data/{test/aws → spec/aws/unit}/fiber_spec.rb +0 -0
  49. data/{test/aws → spec/aws/unit}/flow_spec.rb +0 -0
  50. data/{test/aws → spec/aws/unit}/future_spec.rb +0 -0
  51. data/{test/aws → spec/aws/unit}/preinclude_tests.rb +0 -0
  52. data/{test/aws → spec/aws/unit}/rubyflow.rb +0 -0
  53. data/{test/aws → spec/aws/unit}/simple_dfa_spec.rb +0 -0
  54. data/{test/aws → spec}/spec_helper.rb +0 -0
  55. metadata +30 -30
@@ -13,40 +13,43 @@
13
13
  # permissions and limitations under the License.
14
14
  ##
15
15
 
16
- # This module contains the Root of the heirarchy for calls into flow, the AsyncScope
16
+ # This module contains the root of the hierarchy for calls into flow.
17
17
 
18
18
  module AWS
19
19
  module Flow
20
20
  module Core
21
21
 
22
+ # @api private
22
23
  def gate_by_version(version, method, &block)
23
24
  if RUBY_VERSION.send(method, version)
24
25
  block.call
25
26
  end
26
27
  end
27
28
 
28
- # @!visibility private
29
+ # @api private
29
30
  class AsyncScope
30
31
  attr_accessor :stackTrace, :root, :failure, :root_context
31
32
 
33
+ # @api private
32
34
  def is_complete?
33
35
  @root_context.complete
34
36
  end
35
37
 
38
+ # @api private
36
39
  def get_closest_containing_scope
37
40
  @root_error_handler
38
41
  end
39
42
 
43
+ # @api private
40
44
  def cancel(error); @root_error_handler.cancel(error); end
41
45
 
46
+ # @api private
42
47
  def initialize(&block)
43
48
  @root_context = RootAsyncScope.new
44
49
 
45
-
46
-
47
50
  # 1 for the function that skips frames
48
51
  # 1 for the create function
49
- # 1 for the the initialize of the backtrace
52
+ # 1 for the initialize of the backtrace
50
53
 
51
54
  # "./lib/aws/rubyflow/asyncBacktrace.rb:75:in `caller'"
52
55
  # "./lib/aws/rubyflow/asyncBacktrace.rb:21:in `create'"
@@ -62,11 +65,12 @@ module AWS
62
65
  end
63
66
 
64
67
  # Collects all the heirs of a task for use in async_stack_dump
68
+ # @api private
65
69
  def get_heirs
66
70
  @root_error_handler.get_heirs
67
71
  end
68
72
 
69
- # Execute all queued tasks. If execution of those tasks results in addition of new tasks to the queue, execute
73
+ # Execute all queued tasks. If execution of those tasks results in the addition of new tasks to the queue, execute
70
74
  # them as well.
71
75
  #
72
76
  # Unless there are external dependencies or bugs in the tasks to be executed, a single call to this method
@@ -75,6 +79,7 @@ module AWS
75
79
  # @note In the presence of external dependencies, it is expected that {AsyncScope#eventLoop} is called every
76
80
  # time after a change in the state in a dependency can unblock asynchronous execution.
77
81
  #
82
+ # @api private
78
83
  def eventLoop
79
84
  #TODO Figure out when to raise Done raise "Done" if ! @root_task.alive?
80
85
  raise IllegalStateException, "Already complete" if is_complete?
@@ -82,7 +87,7 @@ module AWS
82
87
  # TODO Does this need to be taken care of? It's supposed to protect
83
88
  # against people having errors that are classes, so like, passing
84
89
  # Exception into cancel. We might want to just catch that at the
85
- # entry point
90
+ # entry point.
86
91
  if @root_context.failure
87
92
  if @root_context.failure.respond_to? :message
88
93
  failure_message = @root_context.failure.message + "\n" +
@@ -96,17 +101,19 @@ module AWS
96
101
  return is_complete?
97
102
  end
98
103
 
104
+ # @api private
99
105
  def <<(task)
100
106
  @root_context << task
101
107
  task.parent = @root_context
102
108
  end
103
109
  end
104
110
 
105
- # @!visibility private
111
+ # @api private
106
112
  class RootAsyncScope < FlowFiber
107
113
 
108
114
  attr_accessor :backtrace, :failure, :executor, :complete
109
115
 
116
+ # @api private
110
117
  def initialize(options = {}, &block)
111
118
  @parent = options[:parent_context]
112
119
  @daemon = options[:daemon]
@@ -118,7 +125,8 @@ module AWS
118
125
  end
119
126
 
120
127
  # The only thing that should be removed from the RootAsyncScope is the
121
- # root BeginRescueEnsure, so upon removal we are complete
128
+ # root BeginRescueEnsure, so upon removal we are complete.
129
+ # @api private
122
130
  def remove(task)
123
131
  @complete = true
124
132
  end
@@ -126,21 +134,25 @@ module AWS
126
134
  # As with remove, the only thing that is under RootAsyncScope should be
127
135
  # the root BeginRescueEnsure, so upon failure we will be complete. Also
128
136
  # sets failure variable for later raising.
137
+ # @api private
129
138
  def fail(task, error)
130
139
  @failure = error
131
140
  @complete = true
132
141
  end
133
142
 
143
+ # @api private
134
144
  def <<(this_task)
135
145
  @executor << this_task
136
146
  end
137
147
 
138
- # Reture self, a RootAsyncScope is the closest containing scope
148
+ # Return self, a RootAsyncScope is the closest containing scope.
149
+ # @api private
139
150
  def get_closest_containing_scope
140
151
  self
141
152
  end
142
153
 
143
- # Call out to the AsyncEventLoop
154
+ # Call out to the AsyncEventLoop.
155
+ # @api private
144
156
  def eventLoop
145
157
  @executor.executeQueuedTasks
146
158
  end
@@ -149,6 +161,7 @@ module AWS
149
161
  private
150
162
  DELEGATED_METHODS = [:push, :<<, :enq, :empty?, :length, :size, :delete, :shift]
151
163
 
164
+ # @api private
152
165
  def method_missing(method_name, *args)
153
166
  if DELEGATED_METHODS.include? method_name
154
167
  @executor.send(method_name, *args)
@@ -158,30 +171,34 @@ module AWS
158
171
  end
159
172
  end
160
173
 
161
- # @!visibility private
174
+ # @api private
162
175
  class AsyncEventLoop
163
176
 
177
+ # @api private
164
178
  def initialize
165
179
  @tasks = []
166
180
  end
167
181
 
182
+ # @api private
168
183
  def remove(task)
169
184
  @tasks.delete(task)
170
185
  end
171
186
  # TODO Make sure that it's okay to fail from the AsyncEventLoop, and that
172
- # this is the correct behavior
187
+ # this is the correct behavior.
173
188
  def fail(task, error)
174
189
  raise error
175
190
  end
191
+
192
+ # @api private
176
193
  def <<(task)
177
194
  @tasks << task
178
195
 
179
196
  end
180
197
 
181
-
182
198
  # TODO should this be synchronized somehow?
183
199
 
184
- # Actually executes the eventLoop
200
+ # Actually executes the eventLoop.
201
+ # @api private
185
202
  def executeQueuedTasks
186
203
  until @tasks.empty?
187
204
  task = @tasks.shift
@@ -189,7 +206,6 @@ module AWS
189
206
  end
190
207
  end
191
208
  end
192
-
193
209
  end
194
210
  end
195
211
  end
@@ -20,16 +20,17 @@ module AWS
20
20
  module Flow
21
21
  module Core
22
22
 
23
- # This class allows asynchronous error handling within the AWS Flow Framework for Ruby. Calling
24
- # {#begin}/{#rescue}/{#ensure} is similar to Ruby's native `begin`/`rescue`/`end` semantics.
23
+ # Enables asynchronous error handling within the AWS Flow Framework for Ruby. Calling {#begin}/{#rescue}/{#ensure}
24
+ # is similar to Ruby's native `begin`/`rescue`/`end` semantics.
25
+ #
25
26
  class BeginRescueEnsure < FlowFiber
26
27
 
27
28
  extend SimpleDFA
28
- attr_accessor :parent, :begin_task, :ensure_task, :rescue_tasks,
29
- :rescue_exceptions, :failure, :cancelled, :heirs, :nonDaemonHeirsCount, :executor, :result
29
+ attr_accessor :parent, :begin_task, :ensure_task, :rescue_hash,
30
+ :failure, :cancelled, :heirs, :nonDaemonHeirsCount, :executor, :result
30
31
  attr_reader :backtrace, :__context__
31
32
 
32
- # Create a new BeginRescueEnsure object, with the provided options.
33
+ # Create a new `BeginRescueEnsure` object, with the provided options.
33
34
  #
34
35
  # @param options
35
36
  # Options to set for the class.
@@ -37,13 +38,13 @@ module AWS
37
38
  # @option options [Object] :parent
38
39
  # The parent object.
39
40
  #
41
+ # @api private
40
42
  def initialize(options = {})
41
43
  # We have two different arrays, rather than a hash,
42
44
  # because we want to ensure that we process the rescues in the order
43
45
  # they are written, and because prior to Ruby 1.9, hashes will not
44
46
  # return their elements in the order they were inserted.
45
- @rescue_exceptions = []
46
- @rescue_tasks = []
47
+ @rescue_hash = {}
47
48
  @parent = options[:parent] || Fiber.current.__context__
48
49
  @current = @parent
49
50
  @executor = @parent.executor
@@ -57,16 +58,16 @@ module AWS
57
58
  end
58
59
 
59
60
 
60
- # @!visibility private
61
+ # @api private
61
62
  def is_daemon?
62
63
  false
63
64
  end
64
65
 
65
66
 
66
- # @!visibility private
67
+ # @api private
67
68
  def <<(async_task)
68
69
  # Not going to include the promise to wait for, as it would appear that
69
- # Fibers can wait on futures from their point of origin as part of their
70
+ # fibers can wait on futures from their point of origin as part of their
70
71
  # implementation, as opposed to adding the callback here.
71
72
  check_closed
72
73
  if ! @heirs.member? async_task
@@ -79,15 +80,15 @@ module AWS
79
80
  self
80
81
  end
81
82
 
82
- # @!visibility private
83
+ # @api private
83
84
  def get_closest_containing_scope
84
- # BRE's are special in that they act as a containing scope, so that things
85
- # created in BRE's treat it as the parent, so that it can track the heirs
86
- # correctly and close only when nonDaemonHeirsCount is 0
85
+ # BeginRescueEnsure's are special in that they act as a containing scope, so that things
86
+ # created in BeginRescueEnsure's treat it as the parent, so that it can track the heirs
87
+ # correctly and close only when nonDaemonHeirsCount is 0.
87
88
  self
88
89
  end
89
90
 
90
- # @!visibility private
91
+ # @api private
91
92
  def check_closed
92
93
  raise IllegalStateException, @failure if @current_state == :closed
93
94
  end
@@ -100,6 +101,7 @@ module AWS
100
101
  # @param error
101
102
  # The error associated with the failure.
102
103
  #
104
+ # @api private
103
105
  def fail(this_task, error)
104
106
  check_closed
105
107
  if ( ! (error.class <= CancellationException) || @failure == nil && !@daemondCausedCancellation)
@@ -109,38 +111,39 @@ module AWS
109
111
  end
110
112
  task_out = @heirs.delete?(this_task)
111
113
  raise "There was a task attempted to be removed from a BRE, when the BRE did not have that task as an heir" unless task_out
112
- @nonDaemonHeirsCount -= 1 if ! this_task.is_daemon?
114
+ @nonDaemonHeirsCount -= 1 unless this_task.is_daemon?
113
115
  cancelHeirs
114
116
  update_state
115
117
  end
116
118
 
117
- # Removes the task and updates the state
119
+ # Removes the task and updates the state.
118
120
  #
119
121
  # @param this_task
120
122
  # The task to remove.
121
123
  #
124
+ # @api private
122
125
  def remove(this_task)
123
126
  check_closed
124
127
 
125
128
  task_out = @heirs.delete?(this_task)
126
129
  raise "There was a task attempted to be removed from a BRE, when the BRE did not have that task as an heir" unless task_out
127
- @nonDaemonHeirsCount -= 1 if ! this_task.is_daemon?
130
+ @nonDaemonHeirsCount -= 1 unless this_task.is_daemon?
128
131
  update_state
129
132
  end
130
133
 
131
- # @!visibility private
134
+ # @api private
132
135
  def cancelHeirs
133
136
  toCancel = @heirs.dup
134
137
  toCancel.each { |heir| heir.cancel(@failure) }
135
138
  end
136
139
 
137
- # @!visibility private
140
+ # @api private
138
141
  def merge_stacktraces(failure, this_backtrace, error)
139
142
  backtrace = AsyncBacktrace.create_from_exception(this_backtrace, error)
140
143
  failure.set_backtrace(backtrace.backtrace) if backtrace
141
144
  end
142
145
 
143
- # @!visibility private
146
+ # @api private
144
147
  def cancel(error)
145
148
  if @current_state == :created
146
149
  @current_state = :closed
@@ -159,8 +162,8 @@ module AWS
159
162
  end
160
163
  end
161
164
 
162
- # Actually runs the BRE, by going through the DFA with the symbol :run.
163
- # @!visibility private
165
+ # Actually runs the BeginRescueEnsure, by going through the data flow analysis with the symbol :run.
166
+ # @api private
164
167
  def run
165
168
  this_failure = @failure
166
169
  begin
@@ -178,13 +181,13 @@ module AWS
178
181
  end
179
182
  end
180
183
 
181
- # @!visibility private
184
+ # @api private
182
185
  def alive?
183
186
  @current_state != :closed
184
187
  end
185
188
 
186
- # Updates the state based on the most recent transitions in the DFA
187
- # @!visibility private
189
+ # Updates the state based on the most recent transitions in the data flow analysis.
190
+ # @api private
188
191
  def update_state
189
192
  #TODO ? Add the ! @executed part
190
193
  #return if @current_state == :closed || ! @executed
@@ -199,7 +202,7 @@ module AWS
199
202
  end
200
203
  end
201
204
 
202
- # @!visibility private
205
+ # @api private
203
206
  def get_heirs
204
207
  # TODO fix this so it returns string instead of printing to stdout
205
208
  str = "I am a BeginRescueEnsure with #{heirs.length} heirs
@@ -209,7 +212,7 @@ module AWS
209
212
  # (@heirs.each(&:get_heirs) + [self]).flatten
210
213
  end
211
214
 
212
- # @!visibility private
215
+ # @api private
213
216
  init(:created)
214
217
  {
215
218
  [:created, :run] => lambda { |bre| bre.current_state = :begin; bre.run },
@@ -226,15 +229,15 @@ module AWS
226
229
  # Emulates the behavior of the actual Ruby rescue, see
227
230
  # http://Ruby-doc.org/docs/ProgrammingRuby/html/tut_exceptions.html
228
231
  # for more details
229
- bre.rescue_exceptions.each_index do |index|
232
+ bre.rescue_hash.each_pair do |exception_class, exception_function|
230
233
  this_failure = bre.failure
231
234
  failure_class = bre.failure.is_a?(Exception) ? bre.failure.class : bre.failure
232
- if failure_class <= bre.rescue_exceptions[index]
235
+ # TODO change <= to something more explicit about subclass check
236
+ unless exception_class.select { |x| failure_class <= x }.empty?
233
237
  bre.result.unset
234
238
  bre.failure = nil
235
- task = bre.rescue_tasks[index]
239
+ task = exception_function
236
240
  bre << Task.new(bre) { bre.result.set(task.call(this_failure)) }
237
- # bre.rescue_tasks[index].call(this_failure)
238
241
  break
239
242
  end
240
243
  end
@@ -257,7 +260,7 @@ module AWS
257
260
  # That is, any transition from closed leads back to itself
258
261
  define_general(:closed) { |t| t.current_state = :closed }
259
262
 
260
- # Binds the block to the a lambda to be called when we get to the begin part of the DFA
263
+ # Binds the block to a lambda to be called when we get to the begin part of the data flow analysis.
261
264
  #
262
265
  # @param block
263
266
  # The code block to be called when asynchronous *begin* starts.
@@ -268,24 +271,24 @@ module AWS
268
271
  @begin_task = Task.new(self) { @result.set(block.call) }
269
272
  end
270
273
 
271
- # Binds the block to the a lambda to be called when we get to the rescue part of the DFA
274
+ # Binds the block to a lambda to be called when we get to the rescue part of the DFA
272
275
  #
273
- # @param error_type
274
- # The error type.
276
+ # @param error_types
277
+ # The error types.
275
278
  #
276
279
  # @param block
277
280
  # The code block to be called when asynchronous *rescue* starts.
278
281
  #
279
- def rescue(error_type, block)
282
+ def rescue(error_types, block)
283
+ error_types = [error_types] unless error_types.is_a? Array
280
284
  this_task = lambda { |failure| block.call(failure) }
281
- if @rescue_exceptions.include? error_type
282
- raise "You have already registered #{error_type}!"
285
+ if @rescue_hash.key? error_types
286
+ raise "You have already registered #{error_types}"
283
287
  end
284
- @rescue_exceptions << error_type
285
- @rescue_tasks << this_task
288
+ @rescue_hash[error_types] = this_task
286
289
  end
287
290
 
288
- # Binds the block to the a lambda to be called when we get to the ensure part of the DFA
291
+ # Binds the block to a lambda to be called when we get to the ensure part of the data flow analysis.
289
292
  #
290
293
  # @param block
291
294
  # The code block to be called when asynchronous *ensure* starts.
@@ -300,7 +303,7 @@ module AWS
300
303
  end
301
304
  end
302
305
 
303
- # Class to ensure that all the inner guts of BRE aren't exposed. This function is passed in when error_handler is
306
+ # Ensures that {BeginRescueEnsure} isn't exposed directly. This function is passed in when {#error_handler} is
304
307
  # called, like this:
305
308
  #
306
309
  # error_handler do |t|
@@ -309,21 +312,23 @@ module AWS
309
312
  # t.ensure { trace << t.begin_task }
310
313
  # end
311
314
  #
312
- # The *t* that is passed in is actually a {BeginRescueEnsureWrapper}, which will only pass begin/rescue/ensure
313
- # onto the {BeginRescueEnsure} class itself.
315
+ # In this example, *t* is a {BeginRescueEnsureWrapper} which passes the begin/rescue/ensure calls to the
316
+ # {BeginRescueEnsure} class itself.
314
317
  #
318
+ # @api private
315
319
  class BeginRescueEnsureWrapper < FlowFiber
316
- # Also has a few methods to ensure Fiber-ness, such as get_heirs and cancel.
320
+ # Also has a few methods to ensure fiber-ness, such as get_heirs and cancel.
317
321
  attr_reader :__context__
318
322
 
319
- # Creates a new BeginRescueEnsureWrapper instance.
323
+ # Creates a new `BeginRescueEnsureWrapper` instance.
320
324
  #
321
325
  # @param block
322
- # A code block to be called.
326
+ # *Required*. A code block to be called.
323
327
  #
324
328
  # @param begin_rescue_ensure
325
329
  # The {BeginRescueEnsure} instance to wrap.
326
330
  #
331
+ # @api private
327
332
  def initialize(block, begin_rescue_ensure)
328
333
  @beginRescueEnsure = begin_rescue_ensure
329
334
  @__context__ = @beginRescueEnsure
@@ -337,26 +342,24 @@ module AWS
337
342
  end
338
343
  end
339
344
 
340
- # @!visibility private
345
+ # @api private
341
346
  def get_heirs
342
347
  p "I am a BREWrapper"
343
348
  return
344
349
  end
345
350
 
351
+ # @api private
346
352
  def cancel(error_type)
347
353
  @beginRescueEnsure.parent.cancel(self)
348
354
  end
349
355
 
350
- # @!visibility private
351
- #
352
- # @return [false]
353
- # Always returns `false`.
354
- #
356
+ # @api private
355
357
  def is_daemon?
356
358
  false
357
359
  end
358
360
 
359
361
  # Gets the parent of the {BeginRescueEnsure} instance held by this class.
362
+ # @api private
360
363
  def get_closest_containing_scope
361
364
  @beginRescueEnsure.parent
362
365
  end
@@ -368,15 +371,17 @@ module AWS
368
371
  def ensure(&block) @beginRescueEnsure.ensure(block) end
369
372
 
370
373
  # (see BeginRescueEnsure#rescue)
371
- def rescue(error_type, &block)
372
- @beginRescueEnsure.rescue(error_type, block)
374
+ def rescue(*error_types, &block)
375
+ @beginRescueEnsure.rescue(error_types, block)
373
376
  end
374
377
 
375
378
  private
376
379
  attr_accessor :beginRescueEnsure
377
380
  end
378
381
 
382
+ # @api private
379
383
  class DaemonBeginRescueEnsure < BeginRescueEnsure
384
+ # @api private
380
385
  def is_daemon?
381
386
  true
382
387
  end