
doc8 is a linter for documents and used in openstack-manuals. It is better to enforce document linters for simple checking. This change is to add doc8 in tox file and fix line too long in some files. The current rules are as bellow: - invalid rst format - D000 - lines should not be longer than 79 characters - D001 - no trailing whitespace - D002 - no tabulation for indentation - D003 - no carriage returns (use unix newlines) - D004 - no newline at end of file - D005 Change-Id: Ibba3f0e1c3f724563deb27bbf4f13a8040799687 Closes-bug: #1709571
149 lines
5.7 KiB
ReStructuredText
149 lines
5.7 KiB
ReStructuredText
=====================================
|
|
How to work with asynchronous actions
|
|
=====================================
|
|
|
|
*******
|
|
Concept
|
|
*******
|
|
|
|
.. image:: /img/Mistral_actions.png
|
|
|
|
During a workflow execution Mistral eventually runs actions. Action
|
|
is a particular function (or a piece of work) that a workflow task
|
|
is associated to.
|
|
|
|
Actions can be synchronous and asynchronous.
|
|
|
|
Synchronous actions are actions that get completed without a 3rd party,
|
|
i.e. by Mistral itself. When Mistral engine schedules to run a synchronous
|
|
action it sends its definition and parameters to Mistral executor, then
|
|
executor runs it and upon its completion sends a result of the action back
|
|
to Mistral engine.
|
|
|
|
In case of asynchronous actions executor doesn't send a result back to Mistral.
|
|
In fact, the concept of asynchronous action assumes that a result won't be
|
|
known at a time when executor is running it. It rather assumes that action
|
|
will just delegate actual work to a 3rd party which can be either a human or
|
|
a computer system (e.g. a web service). So an asynchronous action's run()
|
|
method is supposed to just send a signal to something that is capable of
|
|
doing required job.
|
|
|
|
Once the 3rd party has done the job it takes responsibility to send result
|
|
of the action back to Mistral via Mistral API. Effectively, the 3rd party just
|
|
needs to update the state of corresponding action execution object. To make it
|
|
possible it must know corresponding action execution id.
|
|
|
|
It's worth noting that from Mistral engine perspective the schema is
|
|
essentially the same in case of synchronous and asynchronous actions.
|
|
If action is synchronous, then executor immediately sends a result back
|
|
with RPC mechanism (most often, a message queue as a transport) to Mistral
|
|
engine after action completion. But engine itself is not waiting anything
|
|
proactively, its architecture is fully on asynchronous messages. So in case
|
|
of asynchronous action the only change is that executor is not responsible
|
|
for sending action result, something else takes over.
|
|
|
|
Let's see what we need to keep in mind when working with asynchronous actions.
|
|
|
|
******
|
|
How to
|
|
******
|
|
|
|
Currently, Mistral comes with one asynchronous action out of the box,
|
|
"mistral_http".
|
|
There's also "async_noop" action that is also asynchronous but it's mostly
|
|
useful for testing purposes because it does nothing. "mistral_http" is an
|
|
asynchronous version of action "http" sending HTTP requests. Asynchrony is
|
|
controlled by action's method is_sync() which should return *True* for
|
|
synchronous actions and *False* for asynchronous.
|
|
|
|
Let's see how "mistral_http" action works and how to use it step by step.
|
|
|
|
We can imagine that we have a simple web service playing a role of 3rd party
|
|
system mentioned before accessible at http://my.webservice.com. And if we send
|
|
an HTTP request to that url then our web service will do something useful.
|
|
To keep it simple, let's say our web service just calculates a sum of two
|
|
numbers provided as request parameters "a" and "b".
|
|
|
|
1. Workflow example
|
|
===================
|
|
|
|
.. code-block:: yaml
|
|
|
|
---
|
|
version: '2.0'
|
|
|
|
my_workflow:
|
|
tasks:
|
|
one_plus_two:
|
|
action: mistral_http url=http://my.webservice.com
|
|
input:
|
|
params:
|
|
a: 1
|
|
b: 2
|
|
|
|
So our workflow has just one task "one_plus_two" that sends a request to
|
|
our web service and passes parameters "a" and "b" in a query string. Note
|
|
that we specify "url" right after action name but "params" in a special
|
|
section "input". This is because there's no one-line syntax for dictionaries
|
|
currently in Mistral. But both "url" and "params" are basically just parameters
|
|
of action "mistral_http".
|
|
|
|
It is important to know that when "mistral_http" action sends a request it
|
|
includes special HTTP headers that help identify action execution object.
|
|
These headers are:
|
|
|
|
- **Mistral-Workflow-Name**
|
|
- **Mistral-Workflow-Execution-Id**
|
|
- **Mistral-Task-Id**
|
|
- **Mistral-Action-Execution-Id**
|
|
- **Mistral-Callback-URL**
|
|
|
|
The most important one is "Mistral-Action-Execution-Id" which contains an id
|
|
of action execution that we need to calculate result for. Using that id a 3rd
|
|
party can deliver a result back to Mistral once it's calculated. If a 3rd party
|
|
is a computer system it can just call Mistral API via HTTP using header
|
|
"Mistral-Callback-URL" which contains a base URL. However, a human can also do
|
|
it, the simplest way is just to use Mistral CLI.
|
|
|
|
Of course, this is a practically meaningless example. It doesn't make sense to
|
|
use asynchronous actions for simple arithmetic operations. Real examples when
|
|
asynchronous actions are needed may include:
|
|
|
|
- **Analysis of big data volumes**. E.g. we need to run an external
|
|
reporting tool.
|
|
- **Human interaction**. E.g. an administrator needs to approve allocation of
|
|
resources.
|
|
|
|
In general, this can be anything that takes significant time, such as hours,
|
|
days or weeks. Sometimes duration of a job may be even unpredictable (it's
|
|
reasonable though to try to limit such jobs with timeout policy in practice).
|
|
The key point here is that Mistral shouldn't try to wait for completion of
|
|
such job holding some resources needed for that in memory.
|
|
|
|
An important aspect of using asynchronous actions is that even when we interact
|
|
with 3rd party computer systems a human can still trigger action completion by
|
|
just calling Mistral API.
|
|
|
|
|
|
2. Pushing action result to Mistral
|
|
===================================
|
|
|
|
Using CLI:
|
|
|
|
.. code-block:: console
|
|
|
|
$ mistral action-execution-update <id> --state SUCCESS --output 3
|
|
|
|
This command will update "state" and "output" of action execution object with
|
|
corresponding id. That way Mistral will know what the result of this action
|
|
is and decide how to proceed with workflow execution.
|
|
|
|
Using raw HTTP::
|
|
|
|
POST <Mistral-Callback-URL>/v2/action-executions/<id>
|
|
|
|
{
|
|
"state": "SUCCESS",
|
|
"output": 3
|
|
}
|