aws-flow 1.0.5 → 1.0.6

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.
@@ -13,12 +13,6 @@
13
13
  # permissions and limitations under the License.
14
14
  #++
15
15
 
16
- # @!visibility private
17
- def require_all(path)
18
- glob = File.join(path, "*.rb")
19
- Dir[glob].each { |f| require f}
20
- Dir[glob].map { |f| File.basename(f) }
21
- end
22
16
 
23
17
  require 'aws/flow'
24
18
  include AWS::Flow::Core
@@ -27,9 +21,6 @@ require 'aws-sdk'
27
21
  # Setting the user-agent as ruby-flow for all calls to the service
28
22
  AWS.config(:user_agent_prefix => "ruby-flow")
29
23
 
30
- #LOAD_PATH << File.dirname(File.expand_path(__FILE__))
31
-
32
-
33
24
  require "aws/decider/utilities"
34
25
  require "aws/decider/worker"
35
26
  require 'aws/decider/generic_client'
@@ -132,7 +132,7 @@ module AWS
132
132
  # @!visibility private
133
133
  def get_next_state_machine_which_will_schedule(list)
134
134
  return if list.empty?
135
- ele = list.first
135
+ ele = list.shift
136
136
  ele = list.shift until (list.empty? || ele.get_decision != nil)
137
137
  ele
138
138
  end
@@ -159,18 +159,19 @@ module AWS
159
159
  end
160
160
  decision_state_machine.consume(:handle_decision_task_started_event)
161
161
  decision_state_machine = next_decision_state_machine
162
- if (next_decision_state_machine != nil &&
163
- count < DecisionHelper.maximum_decisions_per_completion)
164
- next_decision_state_machine.consume(:handle_decision_task_started_event)
165
- end
162
+ end
163
+ if (next_decision_state_machine != nil &&
164
+ count < DecisionHelper.maximum_decisions_per_completion)
165
+ next_decision_state_machine.consume(:handle_decision_task_started_event)
166
166
  end
167
167
  end
168
168
 
169
- # @!visibility private
170
- def method_missing(method_name, *args)
171
- if [:[]=, :[]].include? method_name
172
- @decision_map.send(method_name, *args)
173
- end
169
+
170
+ def [](key)
171
+ return @decision_map[key]
172
+ end
173
+ def []=(key, val)
174
+ return @decision_map[key] = val
174
175
  end
175
176
 
176
177
  # Returns the activity ID for a scheduled activity
@@ -336,7 +337,7 @@ module AWS
336
337
  #
337
338
  def complete_workflow
338
339
  return unless @completed && ! @unhandled_decision
339
- decision_id = DecisionID.new(:Self, nil)
340
+ decision_id = [:SELF, nil]
340
341
  if @failure
341
342
  @decision_helper[decision_id] = make_fail_decision(decision_id, @failure)
342
343
  elsif @cancel_requested
@@ -459,7 +460,7 @@ module AWS
459
460
 
460
461
  def handle_closing_failure
461
462
  @unhandled_decision = true
462
- @decision_helper[:SELF, nil].consume(:handle_initiation_failed_event)
463
+ @decision_helper[[:SELF, nil]].consume(:handle_initiation_failed_event)
463
464
  end
464
465
 
465
466
  # Handler for the `:CompleteWorkflowExecutionFailed` event.
@@ -198,7 +198,7 @@ module AWS
198
198
  :consume_symbol => :handle_completion_event,
199
199
  :decision_helper_scheduled => :scheduled_signals,
200
200
  :handle_open_request => lambda do |event, open_request|
201
- workflow_execution = AWS::SimpleWorkflow::WorkflowExecution.new("",event.attributes.workflow_id, event.attributes.run_id)
201
+ workflow_execution = AWS::Flow::MinimalWorkflowExecution.new("",event.attributes.workflow_id, event.attributes.run_id)
202
202
  failure = SignalExternalWorkflowException(event.id, workflow_execution, event.attributes.cause)
203
203
  open_request.completion_handle.fail(failure)
204
204
  end
@@ -215,7 +215,7 @@ module AWS
215
215
  :consume_symbol => :handle_initiation_failed_event,
216
216
  :decision_helper_scheduled => :scheduled_external_workflows,
217
217
  :handle_open_request => lambda do |event, open_request|
218
- workflow_execution = AWS::SimpleWorkflow::WorkflowExecution.new("",event.attributes.workflow_id, nil)
218
+ workflow_execution = AWS::Flow::MinimalWorkflowExecution.new("",event.attributes.workflow_id, nil)
219
219
  workflow_type = event.attributes.workflow_type
220
220
  cause = event.attributes.cause
221
221
  failure = StartChildWorkflowFailedException.new(event.id, workflow_execution, workflow_type, cause)
@@ -80,12 +80,16 @@ module AWS
80
80
  elsif block.arity == 0
81
81
  modified_options = Proc.new do
82
82
  result = block.call
83
+ # We need to copy the hash to make sure that we don't mutate it
84
+ result = result.dup
83
85
  result[:return_on_start] = true
84
86
  result
85
87
  end
86
88
  # Otherwise, it will expect an options object passed in, and will do things on that object. So make our new Proc do that, and add an option
87
89
  else modified_options = Proc.new do |x|
88
90
  result = block.call(x)
91
+ # We need to copy the hash to make sure that we don't mutate it
92
+ result = result.dup
89
93
  result.return_on_start = true
90
94
  result
91
95
  end
@@ -99,7 +99,10 @@ module AWS
99
99
  raise "This activity worker was told to work on activity type #{activity_type.name}, but this activity worker only knows how to work on #{@activity_definition_map.keys.map(&:name).join' '}" unless activity_implementation
100
100
  output = activity_implementation.execute(task.input, context)
101
101
  @logger.debug "Responding on task_token #{task.task_token} for task #{task}"
102
- if ! activity_implementation.execution_options.manual_completion
102
+ if output.length > 32768
103
+ output = output.slice(0..32767)
104
+ respond_activity_task_failed_with_retry(task.task_token, "You cannot send a response with a result greater thank 32768. Please reduce the response size. The first part of the output is included in the details field.", output )
105
+ elsif ! activity_implementation.execution_options.manual_completion
103
106
  @service.respond_activity_task_completed(:task_token => task.task_token, :result => output)
104
107
  end
105
108
  rescue ActivityFailureException => e
@@ -133,7 +136,7 @@ module AWS
133
136
  end
134
137
 
135
138
  def process_single_task(task)
136
- @service = AWS::SimpleWorkflow.new.client.with_http_handler(AWS::Core::Http::NetHttpHandler.new(:ssl_ca_file => AWS.config.ssl_ca_file))
139
+ @service = AWS::SimpleWorkflow.new.client.with_http_handler(AWS::Core::Http::NetHttpHandler.new(AWS.config.to_h))
137
140
  begin
138
141
  begin
139
142
  execute(task)
@@ -16,7 +16,7 @@
16
16
  module AWS
17
17
  module Flow
18
18
  def self.version
19
- "1.0.5"
19
+ "1.0.6"
20
20
  end
21
21
  end
22
22
  end
@@ -16,11 +16,24 @@
16
16
  module AWS
17
17
  module Flow
18
18
 
19
+ class MinimalDomain
20
+ attr_accessor :name
21
+ def initialize(domain); @domain = domain; end
22
+ end
23
+
24
+ class MinimalWorkflowExecution
25
+ attr_accessor :workflow_id, :run_id, :domain
26
+ def initialize(domain, workflow_id, run_id)
27
+ @domain = domain
28
+ @workflow_id = workflow_id
29
+ @run_id = run_id
30
+ end
31
+ end
19
32
 
20
33
  # A future provided by a [WorkflowExecution](http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SimpleWorkflow/WorkflowExecution.html).
21
34
  #
22
35
  # @!attribute _workflow_execution
23
- # The [WorkflowExecution](http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/SimpleWorkflow/WorkflowExecution.html)
36
+ # A MinimalWorkflowExecution
24
37
  # instance that this future belongs to.
25
38
  #
26
39
  # @!attribute return_value
@@ -35,7 +48,7 @@ module AWS
35
48
  # @param workflow_execution
36
49
  #
37
50
  def initialize(workflow_execution)
38
- @_workflow_execution = workflow_execution
51
+ @_workflow_execution = workflow_execution.dup
39
52
  @return_value = Future.new
40
53
  end
41
54
 
@@ -208,7 +221,8 @@ module AWS
208
221
  get_decision_context
209
222
  options = Utilities::interpret_block_for_options(StartWorkflowOptions, block)
210
223
  workflow_id_future, run_id_future = Future.new, Future.new
211
- output = WorkflowFuture.new(AWS::SimpleWorkflow::WorkflowExecution.new(@domain, workflow_id_future, run_id_future))
224
+ minimal_domain = MinimalDomain.new(@domain.name.to_s)
225
+ output = WorkflowFuture.new(AWS::Flow::MinimalWorkflowExecution.new(minimal_domain, workflow_id_future, run_id_future))
212
226
  options = Utilities::merge_all_options(@options, options)
213
227
  new_options = StartWorkflowOptions.new(options)
214
228
  open_request = OpenRequestInfo.new
@@ -0,0 +1,29 @@
1
+ ##
2
+ # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License").
5
+ # You may not use this file except in compliance with the License.
6
+ # A copy of the License is located at
7
+ #
8
+ # http://aws.amazon.com/apache2.0
9
+ #
10
+ # or in the "license" file accompanying this file. This file is distributed
11
+ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12
+ # express or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ ##
15
+
16
+
17
+ # Everything depends on fiber, so we have to require that before anything else
18
+ require 'aws/flow/fiber'
19
+
20
+ # Require everything else
21
+ require 'aws/flow/async_backtrace.rb'
22
+ require 'aws/flow/async_scope.rb'
23
+ require 'aws/flow/begin_rescue_ensure.rb'
24
+ require 'aws/flow/flow_utils.rb'
25
+ require 'aws/flow/future.rb'
26
+ require 'aws/flow/implementation.rb'
27
+ require 'aws/flow/simple_dfa.rb'
28
+ require 'aws/flow/tasks.rb'
29
+ $RUBY_FLOW_FILES = ["async_backtrace.rb","tasks.rb","simple_dfa.rb","implementation.rb","future.rb","flow_utils.rb","begin_rescue_ensure.rb","async_scope.rb"]
@@ -0,0 +1,134 @@
1
+ ##
2
+ # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License").
5
+ # You may not use this file except in compliance with the License.
6
+ # A copy of the License is located at
7
+ #
8
+ # http://aws.amazon.com/apache2.0
9
+ #
10
+ # or in the "license" file accompanying this file. This file is distributed
11
+ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12
+ # express or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ ##
15
+
16
+ # This file contains AsyncBacktrace, which takes care of decorating and properly filtering backtraces
17
+
18
+ module AWS
19
+ module Flow
20
+ module Core
21
+ # @!visibility private
22
+ class AsyncBacktrace
23
+
24
+ # @!visibility private
25
+ def initialize(parent, backtrace)
26
+ @backtrace = AsyncBacktrace.filter(backtrace)
27
+ @parent = parent
28
+ end
29
+
30
+ # @!visibility private
31
+ def backtrace
32
+ if @parent
33
+ AsyncBacktrace.merge(@backtrace, @parent.backtrace)
34
+ else
35
+ @backtrace
36
+ end
37
+ end
38
+
39
+ # @!visibility private
40
+ class << self
41
+
42
+ # @!visibility private
43
+ def create(parent, frames_to_skip)
44
+
45
+ unless @disable_async_backtrace
46
+ b = AsyncBacktrace.caller(frames_to_skip)
47
+ AsyncBacktrace.new(parent, b)
48
+ end
49
+ end
50
+
51
+ # @!visibility private
52
+ def create_from_exception(parent, exception)
53
+ unless @disable_async_backtrace
54
+ AsyncBacktrace.new(parent, exception.backtrace);
55
+ end
56
+ end
57
+
58
+ # Remove all framework related frames after application frames. Keep framework frames before application
59
+ # frames.
60
+ #
61
+ # @todo
62
+ # The correct implementation should not have framework frames before application frames as it is expected to
63
+ # call Kernel.caller with the correct number. But in cases when due to changes this number is not correct
64
+ # the frames are kept to not create confusion.
65
+ #
66
+ def filter(backtrace)
67
+ if @disable_filtering
68
+ backtrace
69
+ else
70
+ do_filter(backtrace)
71
+ end
72
+ end
73
+
74
+ # @!visibility private
75
+ def merge(*backtraces)
76
+ result = []
77
+ backtraces.each do | b |
78
+ if b
79
+ result << "------ continuation ------" if result.size > 0
80
+ result += b
81
+ end
82
+ end
83
+ result
84
+ end
85
+
86
+ # @!visibility private
87
+ def disable_filtering
88
+ @disable_filtering = true
89
+ end
90
+
91
+ # @!visibility private
92
+ def enable_filtering
93
+ @disable_filtering = false
94
+ end
95
+
96
+ # @!visibility private
97
+ def disable
98
+ @disable_async_backtrace = true
99
+ end
100
+
101
+ # @!visibility private
102
+ def enable
103
+ @disable_async_backtrace = false
104
+ end
105
+
106
+ # @!visibility private
107
+ def caller(skip)
108
+ random_var = Kernel.caller 0
109
+ this_stuff = 1.upto(6).map { |x| Kernel.caller(x) }
110
+ other_var = Kernel.caller skip
111
+ Kernel.caller(@disable_filtering ? 0 : skip)
112
+ end
113
+
114
+ private
115
+
116
+ # @!visibility private
117
+ def do_filter(backtrace)
118
+ return nil unless backtrace
119
+ # keep asynchrony frames at the top of the backtrace only
120
+ # then cut all frames starting from asynchrony frame
121
+ skip_asynchrony_frames = false
122
+ @backtrace = backtrace.take_while do |frame|
123
+ if ! $RUBY_FLOW_FILES.select {|file| Regexp.new(file) =~ frame}.empty?
124
+ !skip_asynchrony_frames
125
+ else
126
+ skip_asynchrony_frames = true
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,195 @@
1
+ ##
2
+ # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License").
5
+ # You may not use this file except in compliance with the License.
6
+ # A copy of the License is located at
7
+ #
8
+ # http://aws.amazon.com/apache2.0
9
+ #
10
+ # or in the "license" file accompanying this file. This file is distributed
11
+ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12
+ # express or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ ##
15
+
16
+ # This module contains the Root of the heirarchy for calls into flow, the AsyncScope
17
+
18
+ module AWS
19
+ module Flow
20
+ module Core
21
+
22
+ def gate_by_version(version, method, &block)
23
+ if RUBY_VERSION.send(method, version)
24
+ block.call
25
+ end
26
+ end
27
+
28
+ # @!visibility private
29
+ class AsyncScope
30
+ attr_accessor :stackTrace, :root, :failure, :root_context
31
+
32
+ def is_complete?
33
+ @root_context.complete
34
+ end
35
+
36
+ def get_closest_containing_scope
37
+ @root_error_handler
38
+ end
39
+
40
+ def cancel(error); @root_error_handler.cancel(error); end
41
+
42
+ def initialize(&block)
43
+ @root_context = RootAsyncScope.new
44
+
45
+
46
+
47
+ # 1 for the function that skips frames
48
+ # 1 for the create function
49
+ # 1 for the the initialize of the backtrace
50
+
51
+ # "./lib/aws/rubyflow/asyncBacktrace.rb:75:in `caller'"
52
+ # "./lib/aws/rubyflow/asyncBacktrace.rb:21:in `create'"
53
+ # "./lib/aws/rubyflow/asyncScope.rb:18:in `initialize'"
54
+
55
+ @root_context.backtrace = AsyncBacktrace.create(nil, 3)
56
+ @root_error_handler = BeginRescueEnsure.new(:parent => @root_context)
57
+ begin
58
+ @root_error_handler.begin lambda { block.call if ! block.nil? }
59
+ @root_error_handler.rescue(Exception, lambda { |e| raise e })
60
+ end
61
+ @root_context << @root_error_handler
62
+ end
63
+
64
+ # Collects all the heirs of a task for use in async_stack_dump
65
+ def get_heirs
66
+ @root_error_handler.get_heirs
67
+ end
68
+
69
+ # Execute all queued tasks. If execution of those tasks results in addition of new tasks to the queue, execute
70
+ # them as well.
71
+ #
72
+ # Unless there are external dependencies or bugs in the tasks to be executed, a single call to this method
73
+ # performs the complete asynchronous execution.
74
+ #
75
+ # @note In the presence of external dependencies, it is expected that {AsyncScope#eventLoop} is called every
76
+ # time after a change in the state in a dependency can unblock asynchronous execution.
77
+ #
78
+ def eventLoop
79
+ #TODO Figure out when to raise Done raise "Done" if ! @root_task.alive?
80
+ raise IllegalStateException, "Already complete" if is_complete?
81
+ @root_context.eventLoop
82
+ # TODO Does this need to be taken care of? It's supposed to protect
83
+ # against people having errors that are classes, so like, passing
84
+ # Exception into cancel. We might want to just catch that at the
85
+ # entry point
86
+ if @root_context.failure
87
+ if @root_context.failure.respond_to? :message
88
+ failure_message = @root_context.failure.message + "\n" +
89
+ @root_context.failure.backtrace.join("\n")
90
+ raise @root_context.failure, failure_message
91
+ else
92
+ raise @root_context.failure
93
+ end
94
+ end
95
+
96
+ return is_complete?
97
+ end
98
+
99
+ def <<(task)
100
+ @root_context << task
101
+ task.parent = @root_context
102
+ end
103
+ end
104
+
105
+ # @!visibility private
106
+ class RootAsyncScope < FlowFiber
107
+
108
+ attr_accessor :backtrace, :failure, :executor, :complete
109
+
110
+ def initialize(options = {}, &block)
111
+ @parent = options[:parent_context]
112
+ @daemon = options[:daemon]
113
+ @context = @parent
114
+ @executor = AsyncEventLoop.new
115
+ @task_queue = []
116
+ @complete = false
117
+ @task_queue << Task.new(context, &block) if block
118
+ end
119
+
120
+ # The only thing that should be removed from the RootAsyncScope is the
121
+ # root BeginRescueEnsure, so upon removal we are complete
122
+ def remove(task)
123
+ @complete = true
124
+ end
125
+
126
+ # As with remove, the only thing that is under RootAsyncScope should be
127
+ # the root BeginRescueEnsure, so upon failure we will be complete. Also
128
+ # sets failure variable for later raising.
129
+ def fail(task, error)
130
+ @failure = error
131
+ @complete = true
132
+ end
133
+
134
+ def <<(this_task)
135
+ @executor << this_task
136
+ end
137
+
138
+ # Reture self, a RootAsyncScope is the closest containing scope
139
+ def get_closest_containing_scope
140
+ self
141
+ end
142
+
143
+ # Call out to the AsyncEventLoop
144
+ def eventLoop
145
+ @executor.executeQueuedTasks
146
+ end
147
+
148
+
149
+ private
150
+ DELEGATED_METHODS = [:push, :<<, :enq, :empty?, :length, :size, :delete, :shift]
151
+
152
+ def method_missing(method_name, *args)
153
+ if DELEGATED_METHODS.include? method_name
154
+ @executor.send(method_name, *args)
155
+ else
156
+ super
157
+ end
158
+ end
159
+ end
160
+
161
+ # @!visibility private
162
+ class AsyncEventLoop
163
+
164
+ def initialize
165
+ @tasks = []
166
+ end
167
+
168
+ def remove(task)
169
+ @tasks.delete(task)
170
+ end
171
+ # TODO Make sure that it's okay to fail from the AsyncEventLoop, and that
172
+ # this is the correct behavior
173
+ def fail(task, error)
174
+ raise error
175
+ end
176
+ def <<(task)
177
+ @tasks << task
178
+
179
+ end
180
+
181
+
182
+ # TODO should this be synchronized somehow?
183
+
184
+ # Actually executes the eventLoop
185
+ def executeQueuedTasks
186
+ until @tasks.empty?
187
+ task = @tasks.shift
188
+ task.resume if task.alive?
189
+ end
190
+ end
191
+ end
192
+
193
+ end
194
+ end
195
+ end