aws-flow 1.0.5 → 1.0.6

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