Delete all necessary delayed calls on WF stop

Change-Id: I50d8164aa022a80e255ca4610ef0393ff802663e
Partial-Bug: #1627443
This commit is contained in:
Renat Akhmerov 2016-09-25 10:50:22 +03:00
parent 2bada97acf
commit 2f817e9b70
5 changed files with 125 additions and 11 deletions

View File

@ -240,16 +240,21 @@ def _create_task(wf_ex, wf_spec, task_spec, ctx, task_ex=None,
@profiler.trace('task-handler-refresh-task-state') @profiler.trace('task-handler-refresh-task-state')
def _refresh_task_state(task_ex_id): def _refresh_task_state(task_ex_id):
with db_api.transaction(): with db_api.transaction():
task_ex = db_api.get_task_execution(task_ex_id) task_ex = db_api.load_task_execution(task_ex_id)
if not task_ex:
return
wf_ex = task_ex.workflow_execution
if states.is_completed(wf_ex.state):
return
wf_spec = spec_parser.get_workflow_spec_by_execution_id( wf_spec = spec_parser.get_workflow_spec_by_execution_id(
task_ex.workflow_execution_id task_ex.workflow_execution_id
) )
wf_ctrl = wf_base.get_controller( wf_ctrl = wf_base.get_controller(wf_ex, wf_spec)
task_ex.workflow_execution,
wf_spec
)
state, state_info, cardinality = wf_ctrl.get_logical_task_state( state, state_info, cardinality = wf_ctrl.get_logical_task_state(
task_ex task_ex

View File

@ -81,7 +81,10 @@ def cancel_workflow(wf_ex, msg=None):
def _check_and_complete(wf_ex_id): def _check_and_complete(wf_ex_id):
# Note: This method can only be called via scheduler. # Note: This method can only be called via scheduler.
with db_api.transaction(): with db_api.transaction():
wf_ex = db_api.get_workflow_execution(wf_ex_id) wf_ex = db_api.load_workflow_execution(wf_ex_id)
if not wf_ex or states.is_completed(wf_ex.state):
return
wf = workflows.Workflow( wf = workflows.Workflow(
db_api.get_workflow_definition(wf_ex.workflow_id), db_api.get_workflow_definition(wf_ex.workflow_id),
@ -167,6 +170,10 @@ def set_workflow_state(wf_ex, state, msg=None):
) )
def _get_completion_check_key(wf_ex):
return 'wfh_on_c_a_c-%s' % wf_ex.id
@profiler.trace('workflow-handler-schedule-check-and-complete') @profiler.trace('workflow-handler-schedule-check-and-complete')
def _schedule_check_and_complete(wf_ex, delay=0): def _schedule_check_and_complete(wf_ex, delay=0):
"""Schedules workflow completion check. """Schedules workflow completion check.
@ -183,8 +190,7 @@ def _schedule_check_and_complete(wf_ex, delay=0):
:param delay: Minimum amount of time before task completion check :param delay: Minimum amount of time before task completion check
should be made. should be made.
""" """
# TODO(rakhmerov): update docstring key = _get_completion_check_key(wf_ex)
key = 'wfh_on_c_a_c-%s' % wf_ex.id
scheduler.schedule_call( scheduler.schedule_call(
None, None,

View File

@ -398,7 +398,14 @@ class DefaultEngineTest(base.DbTestCase):
def test_stop_workflow_fail(self): def test_stop_workflow_fail(self):
# Start workflow. # Start workflow.
wf_ex = self.engine.start_workflow( wf_ex = self.engine.start_workflow(
'wb.wf', {'param1': 'Hey', 'param2': 'Hi'}, task_name="task2") 'wb.wf',
{
'param1': 'Hey',
'param2': 'Hi'
},
task_name="task2"
)
# Re-read execution to access related tasks. # Re-read execution to access related tasks.
wf_ex = db_api.get_workflow_execution(wf_ex.id) wf_ex = db_api.get_workflow_execution(wf_ex.id)
@ -413,7 +420,14 @@ class DefaultEngineTest(base.DbTestCase):
def test_stop_workflow_succeed(self): def test_stop_workflow_succeed(self):
# Start workflow. # Start workflow.
wf_ex = self.engine.start_workflow( wf_ex = self.engine.start_workflow(
'wb.wf', {'param1': 'Hey', 'param2': 'Hi'}, task_name="task2") 'wb.wf',
{
'param1': 'Hey',
'param2': 'Hi'
},
task_name="task2"
)
# Re-read execution to access related tasks. # Re-read execution to access related tasks.
wf_ex = db_api.get_workflow_execution(wf_ex.id) wf_ex = db_api.get_workflow_execution(wf_ex.id)
@ -427,7 +441,14 @@ class DefaultEngineTest(base.DbTestCase):
def test_stop_workflow_bad_status(self): def test_stop_workflow_bad_status(self):
wf_ex = self.engine.start_workflow( wf_ex = self.engine.start_workflow(
'wb.wf', {'param1': 'Hey', 'param2': 'Hi'}, task_name="task2") 'wb.wf',
{
'param1': 'Hey',
'param2': 'Hi'
},
task_name="task2"
)
# Re-read execution to access related tasks. # Re-read execution to access related tasks.
wf_ex = db_api.get_workflow_execution(wf_ex.id) wf_ex = db_api.get_workflow_execution(wf_ex.id)

View File

@ -582,3 +582,30 @@ class DirectWorkflowEngineTest(base.EngineTestCase):
) )
self.assertIn("Task 'task3' not found", exception.message) self.assertIn("Task 'task3' not found", exception.message)
def test_delete_workflow_completion_check_on_stop(self):
wf_text = """---
version: '2.0'
wf:
tasks:
async_task:
action: std.async_noop
"""
wf_service.create_workflows(wf_text)
wf_ex = self.engine.start_workflow('wf', {})
calls = db_api.get_delayed_calls()
mtd_name = 'mistral.engine.workflow_handler._check_and_complete'
self._assert_single_item(calls, target_method_name=mtd_name)
self.engine.stop_workflow(wf_ex.id, state=states.CANCELLED)
self._await(
lambda:
len(db_api.get_delayed_calls(target_method_name=mtd_name)) == 0
)

View File

@ -811,3 +811,58 @@ class JoinEngineTest(base.EngineTestCase):
self.assertEqual(4, len(task_execs)) self.assertEqual(4, len(task_execs))
self._assert_multiple_items(task_execs, 4, state=states.SUCCESS) self._assert_multiple_items(task_execs, 4, state=states.SUCCESS)
def delete_join_completion_check_on_stop(self):
wf_text = """---
version: '2.0'
wf:
tasks:
task1:
action: std.noop
on-success: join_task
task2:
description: Never ends
action: std.async_noop
on-success: join_task
join_task:
join: all
"""
wf_service.create_workflows(wf_text)
wf_ex = self.engine.start_workflow('wf', {})
tasks = db_api.get_task_executions(workflow_execution_id=wf_ex.id)
self.assertTrue(len(tasks) >= 2)
task1 = self._assert_single_item(tasks, name='task1')
self.await_task_success(task1.id)
# Once task1 is finished we know that join_task must be created.
tasks = db_api.get_task_executions(workflow_execution_id=wf_ex.id)
self._assert_single_item(
tasks,
name='join_task',
state=states.WAITING
)
calls = db_api.get_delayed_calls()
mtd_name = 'mistral.engine.task_handler._refresh_task_state'
self._assert_single_item(calls, target_method_name=mtd_name)
# Stop the workflow.
self.engine.stop_workflow(wf_ex.id, state=states.CANCELLED)
self._await(
lambda:
len(db_api.get_delayed_calls(target_method_name=mtd_name)) == 0
)