summaryrefslogtreecommitdiff
path: root/taskflow/engines/action_engine/engine.py
diff options
context:
space:
mode:
authorJoshua Harlow <harlowja@yahoo-inc.com>2015-11-16 16:27:42 -0800
committerJoshua Harlow <harlowja@gmail.com>2016-01-09 22:42:17 -0800
commit8e8156c488dea8ae876b112c30e41e60da4f5be7 (patch)
treea4c119dda7d2338bd8c63aad593bbb42d24f3e1f /taskflow/engines/action_engine/engine.py
parentf555a35f3081ba492db15d7bda11fbe50f2a8349 (diff)
downloadtaskflow-8e8156c488dea8ae876b112c30e41e60da4f5be7.tar.gz
Allow for alterations in decider 'area of influence'
Christmas came early. Closes-Bug: #1479466 Change-Id: I931d826690c925f022dbfffe9afb7bf41345b1d0
Diffstat (limited to 'taskflow/engines/action_engine/engine.py')
-rw-r--r--taskflow/engines/action_engine/engine.py104
1 files changed, 62 insertions, 42 deletions
diff --git a/taskflow/engines/action_engine/engine.py b/taskflow/engines/action_engine/engine.py
index c65da34..7084f51 100644
--- a/taskflow/engines/action_engine/engine.py
+++ b/taskflow/engines/action_engine/engine.py
@@ -99,37 +99,37 @@ class ActionEngine(base.Engine):
**Engine options:**
- +-------------------+-----------------------+------+-----------+
- | Name/key | Description | Type | Default |
- +===================+=======================+======+===========+
- | defer_reverts | This option lets you | bool | ``False`` |
- | | safely nest flows | | |
- | | with retries inside | | |
- | | flows without retries | | |
- | | and it still behaves | | |
- | | as a user would | | |
- | | expect (for example | | |
- | | if the retry gets | | |
- | | exhausted it reverts | | |
- | | the outer flow unless | | |
- | | the outer flow has a | | |
- | | has a separate retry | | |
- | | behavior). | | |
- +-------------------+-----------------------+------+-----------+
- | inject_transient | When true, values | bool | ``True`` |
- | | that are local to | | |
- | | each atoms scope | | |
- | | are injected into | | |
- | | storage into a | | |
- | | transient location | | |
- | | (typically a local | | |
- | | dictionary), when | | |
- | | false those values | | |
- | | are instead persisted | | |
- | | into atom details | | |
- | | (and saved in a non- | | |
- | | transient manner). | | |
- +-------------------+-----------------------+------+-----------+
+ +----------------------+-----------------------+------+------------+
+ | Name/key | Description | Type | Default |
+ +======================+=======================+======+============+
+ | ``defer_reverts`` | This option lets you | bool | ``False`` |
+ | | safely nest flows | | |
+ | | with retries inside | | |
+ | | flows without retries | | |
+ | | and it still behaves | | |
+ | | as a user would | | |
+ | | expect (for example | | |
+ | | if the retry gets | | |
+ | | exhausted it reverts | | |
+ | | the outer flow unless | | |
+ | | the outer flow has a | | |
+ | | has a separate retry | | |
+ | | behavior). | | |
+ +----------------------+-----------------------+------+------------+
+ | ``inject_transient`` | When true, values | bool | ``True`` |
+ | | that are local to | | |
+ | | each atoms scope | | |
+ | | are injected into | | |
+ | | storage into a | | |
+ | | transient location | | |
+ | | (typically a local | | |
+ | | dictionary), when | | |
+ | | false those values | | |
+ | | are instead persisted | | |
+ | | into atom details | | |
+ | | (and saved in a non- | | |
+ | | transient manner). | | |
+ +----------------------+-----------------------+------+------------+
"""
NO_RERAISING_STATES = frozenset([states.SUSPENDED, states.SUCCESS])
@@ -148,6 +148,12 @@ class ActionEngine(base.Engine):
end-users when doing execution iterations via :py:meth:`.run_iter`.
"""
+ MAX_MACHINE_STATES_RETAINED = 10
+ """
+ During :py:meth:`~.run_iter` the last X state machine transitions will
+ be recorded (typically only useful on failure).
+ """
+
def __init__(self, flow, flow_detail, backend, options):
super(ActionEngine, self).__init__(flow, flow_detail, backend, options)
self._runtime = None
@@ -242,16 +248,21 @@ class ActionEngine(base.Engine):
self.compile()
self.prepare()
self.validate()
- last_state = None
+ # Keep track of the last X state changes, which if a failure happens
+ # are quite useful to log (and the performance of tracking this
+ # should be negligible).
+ last_transitions = collections.deque(
+ maxlen=max(1, self.MAX_MACHINE_STATES_RETAINED))
with _start_stop(self._task_executor, self._retry_executor):
self._change_state(states.RUNNING)
try:
closed = False
machine, memory = self._runtime.builder.build(timeout=timeout)
r = runners.FiniteRunner(machine)
- for (_prior_state, new_state) in r.run_iter(builder.START):
- last_state = new_state
- # NOTE(harlowja): skip over meta-states.
+ for transition in r.run_iter(builder.START):
+ last_transitions.append(transition)
+ _prior_state, new_state = transition
+ # NOTE(harlowja): skip over meta-states
if new_state in builder.META_STATES:
continue
if new_state == states.FAILURE:
@@ -271,15 +282,24 @@ class ActionEngine(base.Engine):
self.suspend()
except Exception:
with excutils.save_and_reraise_exception():
+ LOG.exception("Engine execution has failed, something"
+ " bad must of happened (last"
+ " %s machine transitions were %s)",
+ last_transitions.maxlen,
+ list(last_transitions))
self._change_state(states.FAILURE)
else:
- if last_state and last_state not in self.IGNORABLE_STATES:
- self._change_state(new_state)
- if last_state not in self.NO_RERAISING_STATES:
- it = itertools.chain(
- six.itervalues(self.storage.get_failures()),
- six.itervalues(self.storage.get_revert_failures()))
- failure.Failure.reraise_if_any(it)
+ if last_transitions:
+ _prior_state, new_state = last_transitions[-1]
+ if new_state not in self.IGNORABLE_STATES:
+ self._change_state(new_state)
+ if new_state not in self.NO_RERAISING_STATES:
+ failures = self.storage.get_failures()
+ more_failures = self.storage.get_revert_failures()
+ fails = itertools.chain(
+ six.itervalues(failures),
+ six.itervalues(more_failures))
+ failure.Failure.reraise_if_any(fails)
@staticmethod
def _check_compilation(compilation):