def replay(self, history): """Executes the workflow from the start until it blocks. """ self.reset() self._history = History(history) self._history.parse() workflow_started_event = history[0] args = () kwargs = {} input = workflow_started_event.input if input is None: input = {} args = input.get('args', ()) kwargs = input.get('kwargs', {}) # check if there is a workflow cancellation request if self._history.is_cancel_requested: # list all the running activities cancellable_activities_id = self._history.list_cancellable_activities() if len(cancellable_activities_id) == 0: # nothing to cancel, completing the workflow as cancelled cancel_decision = swf.models.decision.WorkflowExecutionDecision() cancel_decision.cancel() logger.info('Sucessfully canceled the workflow.') return [cancel_decision], {} cancel_activities_decisions = [] for activity_id in cancellable_activities_id: # send cancel request to each of them decision = swf.models.decision.ActivityTaskDecision( 'request_cancel', activity_id=activity_id, ) cancel_activities_decisions.append(decision) return cancel_activities_decisions, {} # handle workflow on start delay if self._workflow.delayed_start_timer > 0: if 'delayed_start_timer' not in self._history._timers: logger.info('Scheduling delayed start decision.') timer = swf.models.decision.TimerDecision( 'start', id='delayed_start_timer', start_to_fire_timeout=str(self._workflow.delayed_start_timer)) self._decisions.append(timer) return self._decisions, {} elif self._history._timers['delayed_start_timer']['state'] != 'fired': # wait for the timer event, no-op logger.info('Timer has not fired yet.') return [], {} if self._history.is_workflow_started: # the workflow has just started self.on_start(args, kwargs) # workflow not cancelled try: result = self.run_workflow(*args, **kwargs) except exceptions.ExecutionBlocked: logger.info('{} open activities ({} decisions)'.format( self._open_activity_count, len(self._decisions), )) return self._decisions, {} except exceptions.TaskException, err: reason = 'Workflow execution error in task {}: "{}"'.format( err.task.name, getattr(err.exception, 'reason', repr(err.exception))) logger.info(reason) details = getattr(err.exception, 'details', None) self.on_failure(reason, details, args, kwargs) decision = swf.models.decision.WorkflowExecutionDecision() if self._workflow.is_daemon: # do not fail daemon workflow logger.info('Task failed. Re-running continue_as_new for the daemon workflow.') decision.continue_as_new( input=input, task_list={ 'name': self.task_list }, task_timeout=str(self._workflow.decision_tasks_timeout), execution_timeout=str(self._workflow.execution_timeout), workflow_type_version=str(self._workflow.version)) else: decision.fail( reason=swf.format.reason(reason), details=swf.format.details(details), ) return [decision], {}
# do not fail daemon workflow logger.info('Unexpected workflow error. Re-running continue_as_new for the daemon workflow.') decision.continue_as_new(input=input, task_list={ 'name': self.task_list }, task_timeout=str(self._workflow.decision_tasks_timeout)) else: decision.fail( reason=swf.format.reason(reason), details=swf.format.details(details), ) return [decision], {} decision = swf.models.decision.WorkflowExecutionDecision() if self._workflow.is_daemon: # do not complete daemon workflow logger.info('Running continue_as_new for the daemon workflow.') decision.continue_as_new(input=input, task_list={ 'name': self.task_list }, task_timeout=str(self._workflow.decision_tasks_timeout)) else: decision.complete(result=swf.format.result(json.dumps(result))) self.on_complete(result, args, kwargs) return [decision], {} def on_start(self, wfargs=None, wfkwargs=None): try: self._workflow.on_start(wfargs, wfkwargs) except: logger.exception('Error executing on_start for workflow.', extra = { 'input_args': json.dumps(wfargs), 'input_kwargs': json.dumps(wfkwargs) }) def on_complete(self, result, wfargs=None, wfkwargs=None): try: