Fix short circuit operator re teardowns#32538
Conversation
| to_skip = [ | ||
| x for x in context["task"].get_direct_relatives(upstream=False) if not x.is_teardown | ||
| ] |
There was a problem hiding this comment.
I wonder instead of getting the tasks again (which is surpringly not that trivial), it may make more sense to simply filter from downstream_tasks
to_skip = [t for t in downstream_tasks if self.task_id in t.upstream_task_ids]There was a problem hiding this comment.
when you say not that trivial isn't it just doing dag.task_dict[x] for x in t.upstream_task_ids which is not much different?
There was a problem hiding this comment.
a different way to optimize would to not create the downstream_tasks list at all in the ignore_downstream_trigger_rules is False case. then we could just pass to skip x for x in context["task"].get_direct_relatives(upstream=False) if not x.is_teardown as a generator since skip method can handle that -- i.e. there's not really a need for selectively calling skip.
but stepping back, just to be clear, i'm not adding any new call to either get_flat_relatives or get_direct_relatives -- both calls were there and i don't increase their likelihood of calling or frequency. i just happened to move to a variable cus it was handy to debug something.
There was a problem hiding this comment.
OK @uranusjr i went with that optimization... PTAL
There was a problem hiding this comment.
note: i removed a debug log which emitted the full list of short circuited tasks so that way we could use generator but if you think that's no bueno LMK.
There was a problem hiding this comment.
ok pushed the optimization / refactor a little further here. hopefully i don't break something. lemme know what you think. restored the debug logging, and now it will debug log the actual lists in either case
| execution_date = dag_run.execution_date | ||
| if TYPE_CHECKING: | ||
| assert isinstance(execution_date, DateTime) |
There was a problem hiding this comment.
I think you can do execution_date = context["logical_date"], that one should be guaranteed to be a pendulum DateTime.
There was a problem hiding this comment.
Logical date is typed as datetime too it seems.
And worse, it does not exist on DagRunPydantic, so mypy complains about that. i'm just keeping the insert
There was a problem hiding this comment.
Ok actually... at least we can use cast("DateTime", dag_run.execution_date) which is a little cleaner than the assert i suppose
|
|
||
| # this let's us avoid an intermediate list unless debug logging | ||
| if self.log.getEffectiveLevel() <= logging.DEBUG: | ||
| self.log.debug("Downstream task IDs %s", to_skip := list(get_tasks_to_skip())) |
There was a problem hiding this comment.
Pushing the boundary of the walrus I see…
But should line 274 be removed?
There was a problem hiding this comment.
Pushing the boundary of the walrus I see…
yes, hehe
But should line 274 be removed?
No because on 274 it is a generator. only if we need to log.debug is there a need to "materialize" it in the form of a list, because then it needs to be logged and passed to skip, which makes another list out of it anyway.
So it's maybe a silly optimization but hey you started it 😛
But so generally it will just be generator here, passed to skip as generator. But if user enables debug logging, then when we log it we render it into a list at that log line, and then pass the same list down to skip.
There was a problem hiding this comment.
Ah OK get_tasks_to_skip() is an iterator. Maybe move the line to below this block instead then?
Personally I’d maybe rename the function or to_skip to be clearer there’s an iterator involved (either iter_tasks_to_skip() or tasks_to_skip_iter)
There was a problem hiding this comment.
Ah OK get_tasks_to_skip() is an iterator. Maybe move the line to below this block instead then?
don't follow -- what would that do?
There was a problem hiding this comment.
just going to merge this... if you want to golf it further, you can tag me in review
Short circuit operator should by default not skip teardowns for which the object task is in scope.
So for example if you have
s1 >> w1 >> w2 >> t1and t1 is teardown for s1, then if w1 is short circuit, w2 should be skipped but not t1.A more interesting scenario is
or
In both of these cases, if op1 is short circuit (and s1 >> t1, s2 >> t2), then tasks
s2, op2, t2should all be skipped but t1 should not! BUT... importantly, we don't need to do the skipping for t2 in short circuit operator. This will be handled automatically by the scheduler when applying the trigger rules (if the setup was skipped, the teardown will be skipped). Handling it here would be more complexity and duplication of logic than we need. Particularly in weirder examples likes1 >> w1 >> t1, w2 >> t1where w2 is short circuit.@vatsrahul1001