Family Triggers

To reduce duplication in the graph is is possible to write dependencies using collections of tasks called families).

This tutorial walks you through writing such dependencies using family triggers.

Explanation

Dependencies between tasks can be written using a qualifier to describe the task state that the dependency refers to (e.g. succeed fail, etc). If a dependency does not use a qualifier then it is assumed that the dependency refers to the succeed state e.g:

bake_bread => sell_bread           # sell_bread is dependent on bake_bread succeeding.
bake_bread:succeed => sell_bread?  # sell_bread is dependent on bake_bread succeeding.
sell_bread:fail? => throw_away     # throw_away is dependent on sell_bread failing.

The left-hand side of a dependency (e.g. sell_bread:fail) is referred to as the trigger.

Note

sell_bread(:succeed) and sell_bread:fail are mutually exclusive outcomes. As both appear in the graph above, it is necessary to use the ? syntax to mark them as Optional Outputs.

When we write a trigger involving a family, special qualifiers are required to specify whether the dependency is concerned with all or any of the tasks in that family reaching the desired state e.g:

  • succeed-all

  • succeed-any

  • fail-all

Such triggers are referred to as family triggers

Example

Create a new workflow called tutorial-family-triggers:

mkdir ~/cylc-src/tutorial-family-triggers
cd ~/cylc-src/tutorial-family-triggers

Paste the following configuration into the flow.cylc file:

[scheduler]
    UTC mode = True # Ignore DST
[scheduling]
    [[graph]]
        R1 = visit_mine => MINERS
[runtime]
    [[visit_mine]]
        script = sleep 5; echo 'off to work we go'

    [[MINERS]]
        script = """
            sleep 5;
            if (($RANDOM % 2)); then
                echo 'Diamonds!'; true;
            else
                echo 'Nothing...'; false;
            fi
        """
    [[doc, grumpy, sleepy, happy, bashful, sneezy, dopey]]
        inherit = MINERS

You have now created a workflow that:

  • Has a visit_mine task that sleeps for 5 seconds then outputs a message.

  • Contains a MINERS family with a command in it that randomly succeeds or fails.

  • Has 7 tasks that inherit from the MINERS family.

Validate, install and run the workflow:

cylc validate .
cylc install
cylc play tutorial-family-triggers

You should see the visit_mine task run, then trigger the members of the MINERS family. Note that some of the MINERS tasks may fail so you will need to stop your workflow using the “stop” button in the UI, or:

cylc stop tutorial-family-triggers

Family Triggering: Success

As you will have noticed by watching the workflow run, some of the tasks in the MINERS family succeed and some fail.

We would like to add a task to sell any diamonds we find, but wait for all the miners to report back first so we only make the one trip.

We can address this by using family triggers. In particular, we are going to use the finish-all trigger to check for all members of the MINERS family finishing, and the succeed-any trigger to check for any of the tasks in the MINERS family succeeding.

Open your flow.cylc file and change the [[graph]] to look like this:

[[graph]]
    R1 = """
        visit_mine => MINERS?
        MINERS:finish-all & MINERS:succeed-any? => sell_diamonds
    """

Then, add the following task to the [runtime] section:

[[sell_diamonds]]
   script = sleep 5

These changes add a sell_diamonds task to the workflow which is run once all the MINERS tasks have finished and if any of them have succeeded.

Save your changes and run your workflow. You should see the new sell_diamonds task being run once all the miners have finished and at least one of them has succeeded. Stop your workflow as described above.

See also

User guide on family triggers and optional outputs.

Family Triggering: Failure

Cylc also allows us to trigger off failure of tasks in a particular family.

We would like to add another task to close down unproductive mineshafts once all the miners have reported back and had time to discuss their findings.

To do this we will make use of family triggers in a similar manner to before.

Open your flow.cylc file and change the [[graph]] to look like this:

[[graph]]
    R1 = """
        visit_mine => MINERS?
        MINERS:finish-all & MINERS:succeed-any? => sell_diamonds
        MINERS:finish-all & MINERS:fail-any? => close_shafts
    """

Alter the [[sell_diamonds]] section to look like this:

[[close_shafts, sell_diamonds]]
    script = sleep 5

These changes add a close_shafts task which is run once all the MINERS tasks have finished and any of them have failed.

Save your changes and run your workflow. You should see the new close_shafts run should any of the MINERS tasks be in the failed state once they have all finished.

Different Triggers

Other family qualifiers beyond those covered in the example are also available.

The following types of “all” qualifier are available:

  • :start-all - all the tasks in the family have started

  • :succeed-all - all the tasks in the family have succeeded

  • :fail-all - all the tasks in the family have failed

  • :finish-all - all the tasks in the family have finished

The following types of “any” qualifier are available:

  • :start-any - at least one task in the family has started

  • :succeed-any - at least one task in the family has succeeded

  • :fail-any - at least one task in the family has failed

  • :finish-any - at least one task in the family has finished

Summary

  • Family triggers allow you to write dependencies for collections of tasks.

  • Like task triggers, family triggers can be based on success, failure, starting and finishing of tasks in a family.

  • Family triggers can trigger off either all or any of the tasks in a family.