I'm reading through the documentation on <triggers...
# ask-community
s
I'm reading through the documentation on triggers and it doesn't seem to me like there's a functionality for the specific use case I'm trying to handle. If I have a task T with 3 upstream dependencies – call them A, B, and C – and I want T to run regardless of the outcome of C (which may occasionally fail or time out), is there a way to specify that? In other words, I don't want T to run if A or B don't succeed, but if C doesn't succeed for whatever reason, that's okay
βž• 1
j
Hi @Sean Talia - Would a control flow/conditional task work for you here?
k
Hey @Sean Talia, I think the approach here is to use a state handler on C to change the status from FAILED to SKIPPED if it fails. SKIPPED will be treated as SUCCESS for the trigger for task T
I guess you have options πŸ˜†
πŸ˜‚ 1
upvote 1
s
@Jenny I don't know if the conditional task is really the right move here because I don't think I'm really trying to split the DAG up here in that way; I could make it work by having a control flow that says "If A and B are successful, then always run T (with C upstream of T), otherwise...execute an empty task that raises a
FAIL
signal?" but at a glance that seems like an anti-prefect way of setting that up
and @Kevin Kho that is clever, I hadn't thought of that; but wouldn't manually changing the state like that somewhat conflate the notion of a task that didn't try to execute (i.e. it was truly skipped) with one that did try to run but for whatever reason just couldn't achieve what it was trying to do?
(no doubt a custom handler could send a notification providing those additional details)
j
Hi @Sean Talia - just had a recommendation from another team member - you can write your own custom trigger. The upstream states dictionary has edges for keys, allowing you to write logic based on upstream task attributes like name. Something like:
Copy code
def custom_trigger(upstream_states):
    if not all(s.is_successful() for edge, s in upstream_states.items() if edge.upstream_task.name != "A"):
        raise signals.TRIGGERFAIL("Not all upstream states were successful.")
    else:
        return True
s
ideally I was hoping to be able to supply something to task C that would indicate it's a "non-critical" task but it sounds like it won't be that simple πŸ˜„
ooooh okay that's cool, I didn't realize you could supply custom triggers like that
just my 2 cents but I do think that somehow the information I'm trying to convey is really more intrinsic to the node represented by C than it is to the edge between C and T; I could conceivably have several tasks
T_1, T_2, ... , T_n
that were downstream of C that I might want to run regardless of the outcome of C, and having to attach a custom trigger to all of those downstream
T_x
tasks seems like overkill...it would be really nice be able to do something like:
Copy code
@task(always_continue=True)
def task_c(...)
I totally get that the behind-the-scenes implementation of this might not be so easy though 😎
j
@Sean Talia given your description of the problem, I have a more straightforward suggestion that I think will achieve your purpose. As I understand it, you want to know that task
C
failed (meaning you want it to enter a Failed state), but you do not want your downstream tasks to treat it as they would a normal failure. I would propose that you add a new task to your graph that essentially convert’s `C`’s failure into a success:
C
β†’
new_task
β†’
T
If you put an
always_run
trigger on your
new_task
and have it simply return
True
(or return C’s data, as appropriate) then you will be able to observe
C's
failure states without passing them to the trigger on
T
, as it will always receive
new_task's
success state.
You can have multiple
T1, T2, T3, T4
all downstream of
new_task
and all responding to its
Success
in the same way, such that you only have to define the dummy task once and reuse this behavior across all downstreams. For convenience, you could write a function that automatically generated the dummy task
always_continue(C)
s
that is...beautifully simple. I think that's the way to go, thank you @Jeremiah!
πŸ‘ 1