Expiring Tasks / Cycles

Cylc is often used to write workflows which monitor real-world events.

For example, this workflow will run the task foo every day at 00:00am:

[scheduling]
   initial cycle point = previous(T00)
   [[graph]]
       P1D = """
           @wall_clock => foo
       """

Sometimes such workflows might get behind, e.g. due to failures or slow task execution. In this situation, it might be necessary to skip a few tasks in order for the workflow to catch up with the real-world time.

Cylc has a concept called expiry which allows tasks to be automatically “expired” if they are running behind schedule. The expiry can be configured as an offset from the cycle time.

Example 1: Skip a whole cycle of tasks

If the workflow gets behind, skip whole cycles of tasks until it catches up.

Get a copy of this example

$ cylc get-resources examples/expiry/one
[meta]
    description = """
        If the workflow runs slowly and the cycle time gets behind the real
        world (wallclock) time, then it will skip cycles until it catches up.

        Either a cycle runs or it is skipped.

        When you start this workflow, the first cycle will be at 00:00am this
        morning so will immediately expire causing the workflow to move onto
        tomorrow's cycle.
    """

[scheduler]
    allow implicit tasks = True

[scheduling]
    # start the workflow at 00:00am this morning
    initial cycle point = previous(T00)

    # the "start" task will "expire" if the cycle time falls behind
    # the wallclock time
    [[special tasks]]
        clock-expire = start

    [[graph]]
        P1D = """
            # the chain of tasks we want to run
            start => a => b => c => d => housekeep

            # wait for the previous cycle to either complete or expire before
            # continuing onto the next cycle
            housekeep[-P1D] | start[-P1D]:expired? => start
        """

Example 2: Skip the remainder of a cycle of tasks

If the workflow gets behind, skip the remainder of the tasks in the cycle, then skip whole cycles of tasks until it catches up.

Get a copy of this example

$ cylc get-resources examples/expiry/two
[meta]
    description = """
        If the workflow runs slowly and the cycle time gets behind the real
        world (wallclock) time, then it will skip tasks until it catches up.

        A cycle may be skipped part way through to allow the workflow to catch
        up faster.

        When this workflow starts up, the first cycle will be one minute ahead
        of the wallclock time. At some point in the cycle, the wallclock time
        will overtake the cycle time and the next task in the chain will
        expire. The workflow will then move onto the next cycle.
    """

[scheduler]
    allow implicit tasks = True

[scheduling]
    # start the workflow at 00:00am this morning
    initial cycle point = PT1M

    # any task in the workflow will "expire" rather than run if the cycle
    # time falls behind the wallclock time
    [[special tasks]]
        clock-expire = start, a, b, c, d, housekeep

    [[graph]]
        P1D = """
            # the chain of tasks we want to run
            start => a => b => c => d => housekeep

            # start the next cycle as soon as the previous cycle has finished
            # OR and task in the previous cycle has expired
            housekeep[-P1D]
                | start[-P1D]:expire?
                | a[-P1D]:expired?
                | b[-P1D]:expired?
                | c[-P1D]:expired?
                | d[-P1D]:expired?
                | housekeep[-P1D]:expired?
            => start
        """

[runtime]
    [[root]]
        script = sleep 12

Example 3: Skip selected tasks in a cycle

If the workflow gets behind, turn off selected tasks to allow it to catch up more quickly.

Get a copy of this example

$ cylc get-resources examples/expiry/three
[meta]
    description = """
        If the workflow runs slowly and the cycle time gets behind the real
        world (wallclock) time, then it will skip selected tasks until it
        catches up.

        In this case, the tasks "b", "c" and "d" will be skipped to help the
        workflow to catch up more quickly.

        When this workflow starts up, the first cycle will be at 00:00am today
        so the "start" task will immediately expire. This will cause tasks
        "b", "c" and "d" to be configured to "skip" rather than run.
    """

[scheduler]
    allow implicit tasks = True

[scheduling]
    # start the workflow at 00:00am this morning
    initial cycle point = previous(T00)
    final cycle point = +P0D

    # the "start" task will "expire" if the cycle time falls behind
    # the wallclock time
    [[special tasks]]
        clock-expire = start

    [[graph]]
        P1D = """
            # the chain of tasks we want to run
            start | start:expired? => a => b => c => d => housekeep
        """

[runtime]
    [[start]]
        # if this task expires, configure the tasks "b", "c" and "d" to
        # "skip" rather than run
        # Note: This task will also be "skipped" if it expires
        [[[events]]]
            expired handlers = cylc broadcast "%(workflow)s" -p "%(point)s" -n b -n c -n d -s "run mode = skip"