Exemplo n.º 1
0
    def handle_exception(self, exception, decision_context):
        """
        Handles exceptions from the event loop.

        The default behavior is to log it, fail the workflow, and continue.  This method only gets used when in
        distributed mode.
        :param exception:
        :return: True if we want to exit, False otherwise
        """
        self.logger.exception('Exception caught while running the event loop.')
        # Reset the decisions that we want to make; we can't schedule new activities and fail a workflow in the same
        # call
        decision_context.decisions = Layer1Decisions()
        decision_context.decisions.fail_workflow_execution(
            reason='Decider exception', details=exception.message[:3000])
        return False
Exemplo n.º 2
0
	def handle_nextPageToken(self):
		# Quick test for nextPageToken
		try:
			if self.decision["nextPageToken"]:
				# nextPageToken should be paging if the decider is configured properly
				#  If there is a nextPageToken
				#  something has gone wrong and terminate the workflow execution with
				#  extreme prejudice
				d = Layer1Decisions()
				reason="nextPageToken found, maximum_page_size of " + str(self.maximum_page_size) + " exceeded"
				d.fail_workflow_execution(reason)
				out = self.conn.respond_decision_task_completed(self.token,d._data)
				self.logger.info(reason)
				self.logger.info('respond_decision_task_completed returned %s' % out)
				self.token = None
				return False
		except KeyError:
			# No nextPageToken, so we did not exceed the maximum_page_size, continue
			pass
    def run_decider(self):
        """
        run one iteration of a simple decision engine
        """
        # Poll for a decision task.
        tries = 0
        while True:
            dtask = self.conn.poll_for_decision_task(self._domain,
                                                     self._task_list,
                                                     reverse_order=True)
            if dtask.get('taskToken') is not None:
                # This means a real decision task has arrived.
                break
            time.sleep(2)
            tries += 1
            if tries > 10:
                # Give up if it's taking too long.  Probably
                # means something is broken somewhere else.
                assert False, 'no decision task occurred'

        # Get the most recent interesting event.
        ignorable = (
            'DecisionTaskScheduled',
            'DecisionTaskStarted',
            'DecisionTaskTimedOut',
        )
        event = None
        for tevent in dtask['events']:
            if tevent['eventType'] not in ignorable:
                event = tevent
                break

        # Construct the decision response.
        decisions = Layer1Decisions()
        if event['eventType'] == 'WorkflowExecutionStarted':
            activity_id = str(uuid.uuid1())
            decisions.schedule_activity_task(
                activity_id,
                self._activity_type_name,
                self._activity_type_version,
                task_list=self._task_list,
                input=event['workflowExecutionStartedEventAttributes']
                ['input'])
        elif event['eventType'] == 'ActivityTaskCompleted':
            decisions.complete_workflow_execution(
                result=event['activityTaskCompletedEventAttributes']['result'])
        elif event['eventType'] == 'ActivityTaskFailed':
            decisions.fail_workflow_execution(
                reason=event['activityTaskFailedEventAttributes']['reason'],
                details=event['activityTaskFailedEventAttributes']['details'])
        else:
            decisions.fail_workflow_execution(
                reason='unhandled decision task type; %r' %
                (event['eventType'], ))

        # Send the decision response.
        r = self.conn.respond_decision_task_completed(
            dtask['taskToken'],
            decisions=decisions._data,
            execution_context=None)
        assert r is None
Exemplo n.º 4
0
 def complete(self, result):
     decisions = self._decisions = Layer1Decisions()
     decisions.complete_workflow_execution(result)
     return self.flush()
Exemplo n.º 5
0
 def fail(self, reason):
     decisions = self._decisions = Layer1Decisions()
     decisions.fail_workflow_execution(reason=str(reason)[:256])
     return self.flush()
Exemplo n.º 6
0
 def restart(self, spec, input, tags):
     decisions = self._decisions = Layer1Decisions()
     spec.restart(decisions, input, tags)
     return self.flush()
Exemplo n.º 7
0
 def __init__(self, swf_client, token, rate_limit=64):
     self._swf_client = swf_client
     self._token = token
     self._rate_limit = rate_limit
     self._decisions = Layer1Decisions()
     self._closed = False
Exemplo n.º 8
0
def get_decision_manager():
    return Layer1Decisions()
Exemplo n.º 9
0
    def start(self):
        """
        Starts the event loop

        This method blocks and runs infinitely.  Call this method to start a decisioner in distributed mode after
        construction.
        :return:
        """

        while True:
            self.logger.debug('Polling')

            try:
                decision_task = self._swf.poll_for_decision_task(
                    domain=self._swf_domain, task_list=self._swf_task_list)

                # No-op if we got no events
                if 'events' not in decision_task:
                    self.logger.debug('Calling the no-op handler')
                    self.handle_no_op(decision_task)
                    continue

                self.logger.debug('Received an decision task')

                # Get full event history
                history = decision_task['events']
                next_page_token = decision_task.get('nextPageToken')
                while next_page_token:
                    self.logger.debug('Polling for additional history...')
                    additional_history = self._swf.poll_for_decision_task(
                        domain=self._swf_domain,
                        task_list=self._swf_task_list,
                        next_page_token=next_page_token)
                    if 'events' in additional_history:
                        history.extend(additional_history['events'])

                    decision_task['events'] = list()
                    next_page_token = additional_history.get('nextPageToken')
            except Exception as e:
                self.logger.exception(
                    'Exception while polling for a task and/or history')
                raise e

            # Populate the context
            try:
                decision_context = self._populate_decision_context(
                    decision_task, history)

                # Here's where we make the context available to the decorated function
                self._scanner.decision_context = decision_context

                # Make sure that timers get them, too
                PTimer.decision_context = decision_context
                PMarker.decision_context = decision_context
                # And markers

            except Exception as e:
                self.logger.exception(
                    'Exception while parsing a workflow history')
                message = str(e)
                if message:
                    message = message[:3000]
                decision_context.decisions = Layer1Decisions()
                decision_context.decisions.fail_workflow_execution(
                    reason='Failed to parse workflow history', details=message)
                self._swf.respond_decision_task_completed(
                    decision_task['taskToken'],
                    decision_context.decisions._data)
                return

            # get the args and run the handle function in a thread
            args = (list(), dict())
            if decision_context.workflow.input:
                self.logger.debug('Unpacking input arguments')
                serialized_args = self._input_data_store.get(
                    decision_context.workflow.input)
                args = self._input_serializer.deserialize_input(
                    serialized_args)

            # Do we have a self that we need to pass in?
            if self._decision_object:
                args[0].insert(0, self._decision_object)

            try:
                finished = False
                result = self._decision_function(*args[0], **args[1])
                finished = True
            except UnfulfilledPromiseException:
                finished = False
            except Exception as e:
                self.logger.debug('Calling the exception handler')
                should_exit = self.handle_exception(e, decision_context)
                if should_exit:
                    self.logger.debug(
                        'Exiting due to return value from handle_exception()')
                    return

            # Is our workflow finished?
            if finished is True:
                swf_result = None
                if result:
                    self.logger.debug('Packing workflow results')
                    serialized_result = self._result_serializer.serialize_result(
                        result)
                    key = '{}-result'.format(decision_context.workflow.run_id)
                    swf_result = self._result_data_store.put(
                        serialized_result, key)
                decision_context.decisions.complete_workflow_execution(
                    result=swf_result)

            self.logger.debug('Returning decisions to SWF.')
            try:
                self._swf.respond_decision_task_completed(
                    decision_task['taskToken'],
                    decision_context.decisions._data)
            except Exception:
                self.logger.exception(
                    'Error when responding with decision tasks')