aws-flow 1.0.8 → 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
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