Conductor Actions

Conductor actions make it possible to build and invoke a series of actions, similar to sequences. However, whereas the components of a sequence action must be specified before invoking the sequence, conductor actions can decide the series of actions to invoke at run time.

In this document, we specify conductor actions and illustrate them with a simple example: a tripleAndIncrement action.

Suppose we define a triple action in a source file triple.js:

function main({ value }) { return { value: value * 3 } }

We create the action triple:

wsk action create triple triple.js

We define an increment action in a source file increment.js:

function main({ value }) { return { value: value + 1 } }

We create the action increment:

wsk action create increment increment.js

Conductor annotation

We define the tripleAndIncrement action in a source file tripleAndIncrement.js:

function main(params) {
    let step = params.$step || 0
    delete params.$step
    switch (step) {
        case 0: return { action: 'triple', params, state: { $step: 1 } }
        case 1: return { action: 'increment', params, state: { $step: 2 } }
        case 2: return { params }
    }
}

We create a conductor action by specifying the conductor annotation:

wsk action create tripleAndIncrement tripleAndIncrement.js -a conductor true

A conductor action is an action with a conductor annotation with a value that is not falsy, i.e., a value that is different from zero, null, false, and the empty string.

At this time, the conductor annotation is ignored on sequence actions.

Because a conductor action is an action, it has all the attributes of an action (name, namespace, default parameters, limits...) and it can be managed as such, for instance using the wsk action CLI commands. It can be part of a package or be a web action.

In essence, the tripleAndIncrement action builds a sequence of two actions by encoding a program with three steps:

  • step 0: invoke the triple action on the input dictionary,
  • step 1: invoke the increment action on the output dictionary from step 1,
  • step 2: return the output dictionary from step 2.

At each step, the conductor action specifies how to continue or terminate the execution by means of a continuation. We explain continuations after discussing invocation and activations.

Invocation

A conductor action is invoked like a regular action, for instance:

wsk action invoke tripleAndIncrement -r -p value 3
{
    "value": 10
}

Blocking and non-blocking invocations are supported. As usual, a blocking invocation may timeout before the completion of the invocation.

Activations

One invocation of the conductor action results in multiple activations, for instance:

wsk action invoke tripleAndIncrement -p value 3
ok: invoked /_/tripleAndIncrement with id 4f91f9ed0d874aaa91f9ed0d87baaa07
wsk activation list

There are six activation records in this example, one matching the activation id returned on invocation (22da217c8e3a4b799a217c8e3a0b79c4) plus five additional records for activations caused by this invocation. The primary activation record is the last one in the list because it has the earliest start time.

The five additional activations are:

  • one activation of the triple action with input { value: 3 } and output { value: 9 },
  • one activation of the increment action with input { value: 9 } and output { value: 10 },
  • three secondary activations of the tripleAndIncrement action.

Causality

We say the invocation of the conductor action is the cause of component action invocations as well as secondary activations of the conductor action. These activations are derived activations.

The cause field of the derived activation records is set to the id for the primary activation record.

Primary activations

The primary activation record for the invocation of a conductor action is a synthetic record similar to the activation record of a sequence action. The primary activation record summarizes the series of derived activations:

  • its result is the result of the last action in the series (possibly unboxed, see below),
  • its logs are the ordered list of component and secondary activations,
  • its duration is the sum of the durations of these activations,
  • its start time is less or equal to the start time of the first derived activation in the series,
  • its end time is greater or equal to the end time of the last derived activation in the series.
wsk activation get 4f91f9ed0d874aaa91f9ed0d87baaa07
ok: got activation 4f91f9ed0d874aaa91f9ed0d87baaa07
{
    "namespace": "guest",
    "name": "composition",
    "version": "0.0.1",
    "subject": "guest",
    "activationId": "4f91f9ed0d874aaa91f9ed0d87baaa07",
    "start": 1516379705819,
    "end": 1516379707803,
    "duration": 457,
    "response": {
        "status": "success",
        "statusCode": 0,
        "success": true,
        "result": {
            "value": 12
        }
    },
    "logs": [
        "3624ad829d4044afa4ad829d40e4af60",
        "a1f58ade9b1e4c26b58ade9b1e4c2614",
        "47cb5aa5e4504f818b5aa5e450ef810f",
        "eaec119273d94087ac119273d90087d0",
        "fd89b99a90a1462a89b99a90a1d62a8e"
    ],
    "annotations": [
        {
            "key": "topmost",
            "value": true
        },
        {
            "key": "path",
            "value": "guest/tripleAndIncrement"
        },
        {
            "key": "conductor",
            "value": true
        },
        {
            "key": "kind",
            "value": "sequence"
        },
        {
            "key": "limits",
            "value": {
                "logs": 10,
                "memory": 256,
                "timeout": 60000
            }
        }
    "publish": false
}

If a component action itself is a sequence or conductor action, the logs contain only the id for the component activation. They do not contain the ids for the activations caused by this component. This is different from nested sequence actions.

Secondary activations

The secondary activations of the conductor action are responsible for orchestrating the invocations of the component actions.

An invocation of a conductor action starts with a secondary activation and alternates secondary activations of this conductor action with invocations of the component actions. It normally ends with a secondary activation of the conductor action. In our example, the five derived activations are interleaved as follows:

  1. secondary tripleAndIncrement activation,
  2. triple activation,
  3. secondary tripleAndIncrement activation,
  4. increment activation,
  5. secondary tripleAndIncrement activation.

Intuitively, secondary activations of the conductor action decide which component actions to invoke by running before, in-between, and after the component actions. In this example, the tripleAndIncrement main function runs three times.

Only an internal error (invocation failure or timeout) may result in an even number of derived activations.

Annotations

Primary activation records include the annotations { key: "conductor", value: true } and { key: "kind", value: "sequence" }. Secondary activation records and activation records for component actions include the annotation { key: "causedBy", value: "sequence" }.

The memory limit annotation in the primary activation record reflects the maximum memory limit across the conductor action and the component actions.

Continuations

A conductor action should return either an error dictionary, i.e., a dictionary with an error field, or a continuation, i.e., a dictionary with up to three fields { action, params, state }. In essence, a continuation specifies what component action to invoke if any, as well as the parameters for this invocation, and the state to preserve until the next secondary activation of the conductor action.

The execution flow in our example is the following:

  1. The tripleAndIncrement action is invoked on the input dictionary { value: 3 }. It returns { action: 'triple', params: { value: 3 }, state: { $step: 1 } } requesting that action triple be invoked on params dictionary { value: 3 }.
  2. The triple action is invoked on dictionary { value: 3 } returning { value: 9 }.
  3. The tripleAndIncrement action is automatically reactivated. The input dictionary for this activation is { value: 9, $step: 1 } obtained by combining the result of the triple action invocation with the state of the prior secondary tripleAndIncrement activation (see below for details). It returns { action: 'increment', params: { value: 9 }, state: { $step: 2 } }.
  4. The increment action is invoked on dictionary { value: 9 } returning { value: 10 }.
  5. The tripleAndIncrement action is automatically reactivated on dictionary { value: 10, $step: 2 } returning { params: { value: 10 } }.
  6. Because the output of the last secondary tripleAndIncrement activation specifies no further action to invoke, this completes the execution resulting in the recording of the primary activation. The result of the primary activation is obtained from the result of the last secondary activation by extracting the value of the params field: { value: 10 }.

Detailed specification

If a secondary activation returns an error dictionary, the conductor action invocation ends and the result of this activation (output and status code) are those of this secondary activation.

In a continuation dictionary, the params field is optional and its value if present should be a dictionary. The action field is optional and its value if present should be a string. The state field is optional and its value if present should be a dictionary. If the value v of the params field is not a dictionary it is automatically boxed into dictionary { value: v }. If the value v of the state field is not a dictionary it is automatically boxed into dictionary { state: v }.

If the action field is defined in the output of the conductor action, the runtime attempts to convert its value (irrespective of its type) into the fully qualified name of an action and invoke this action (using the default namespace if necessary). The action name should be a fully qualified name, which is of the form /namespace/package-name/action-name or /namespace/action-name. Failure to specify a fully qualified name may result in ambiguity or even a parsing error. There are four failure modes:

  • parsing failure,
  • resolution failure,
  • entitlement check failure,
  • internal error (invocation failure or timeout).

In the last failure scenario, the conductor action invocation ends with an internal error status code and an error message describing the reason for the failure.

If there is no error, action is invoked on the params dictionary if specified (auto boxed if necessary) or if not on the empty dictionary. Upon completion of this invocation, the conductor action is activated again. The input dictionary for this activation is a combination of the output dictionary for the component action and the value of the state field from the prior secondary conductor activation. Fields of the state dictionary (auto boxed if necessary) are added to the output dictionary of the component activation, overriding values of existing fields if necessary.

In the first three failures scenarios, the conductor action is activated again. The input dictionary for this activation is a combination of an error object with an error message describing the reason for the failure and the value of the state field from the prior secondary conductor activation (as in the previous scenario).

On the other hand, if the action field is not defined in the output of the conductor action, the conductor action invocation ends. The output for the conductor action invocation is either the value of the params field in the output dictionary of the last secondary activation if defined (auto boxed if necessary) or if absent the complete output dictionary.

Limits

There are limits on the number of component action activations and secondary conductor activations in a conductor action invocation. These limits are assessed globally, i.e., if some components of a conductor action invocation are themselves conductor actions, the limits apply to the combined counts of activations across all the conductor action invocations.

The maximum number n of permitted component activations is equal to the maximum number of components in a sequence action. It is configured via the same configuration parameter. The maximum number of secondary conductor activations is 2n+1.

If the maximum number of permitted component activations is exceeded the conductor action is activated again. The input dictionary for this activation is a combination of an error object with an error message describing the reason for the failure and the value of the state field from the prior secondary conductor activation.

If the maximum number of secondary conductor activations is exceeded, the conductor action invocation ends with an application error status code and an error message describing the reason for the failure.