You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Execution_date not rendering after airflow upgrade #19618
Hi,
We recently upgraded airflow from 2.1.0 to 2.2.2 (2.1.0 to 2.2.0 to 2.2.1 to 2.2.2) and DAGs aren't running as expected. All these DAGs were added before the upgrade itself and they were running fine.
We use execution_date parameter in SparkSubmitOperator which was rendering fine before the upgrade fails now returning None
"{{ (execution_date if execution_date.microsecond > 0 else dag.following_schedule(execution_date)).isoformat() }}"
DAG run fails with the error
jinja2.exceptions.UndefinedError: 'None' has no attribute 'isoformat'
Tried wiping out the database and ran as a fresh DAG but still same error
On a related note, noticed from the airflow doc that {{ execution_date }} is deprecated.
I see that {{ ts }} is equivalent to execution_date isoformat but this is a string type. Do we have an equivalent for execution_date in datetime type ?
OK. Seems prety legit then. Hmm. Interesting - we converted the execution_date to be lazy-proxy object so essentially it should work in a backwards compatible way (correct @uranusjr ?).
I believe I know what the problem is. yes the dag_run.logical_date should work and I know what the problem is: @uranusjr - this is the "coerce_date" in following_schedule:
def following_schedule(self, dttm):
"""
Calculates the following schedule for this dag in UTC.
:param dttm: utc datetime
:return: utc datetime
"""
warnings.warn(
"`DAG.following_schedule()` is deprecated. Use `DAG.next_dagrun_info(restricted=False)` instead.",
category=DeprecationWarning,
stacklevel=2,
)
data_interval = self.infer_automated_data_interval(timezone.coerce_datetime(dttm))
next_info = self.next_dagrun_info(data_interval, restricted=False)
if next_info is None:
return None
return next_info.data_interval.start
Here is coerce_date:
def coerce_datetime(v: Union[None, dt.datetime, DateTime]) -> Optional[DateTime]:
"""Convert whatever is passed in to an timezone-aware ``pendulum.DateTime``."""
if v is None:
return None
if v.tzinfo is None:
v = make_aware(v)
if isinstance(v, DateTime):
return v
return pendulum.instance(v)
I can see why the error happens, but not why the error is wrong. A @once schedule does not have a following schedule (because it runs only once?), so dag.following_schedule(execution_date) is correct to return None. This also should be the case if you use dag_run.logical_date.
Oh I know what’s going on now. We’ve been looking at entirely wrong places.
The change that actually broke the logic is #16141. Previously, a manually-triggered run always has its execution date set to timezone.now(), which has an approximately 99.9999% chance to return a non-zero millisecond, so
But the calendar widget introduced in #16141 does not employ sub-second accurary, so the milliseconds become zero, and all manually-triggered runs now evaluate the expression to
instead, which always fails because a manually triggered run never has a following schedule (both prior and after AIP-39).
Ultimately, using the millisecond field to distinguish between scheduled and manual runs is a poor implementation in the first place. There’s still a 0.0001% chance the detection would fail, even if Airflow never broke that. It is both more semantically correct and reliable to use dag_run.run_type instead.
I think the best we can do here is to add an entry to UPDATING.md to describe this behavioural change.
One correction. (execution_date if execution_date.microsecond > 0 else dag.following_schedule(execution_date)).isoformat()
fails for scheduled runs as well. dag.following_schedule(execution_date) returns null for scheduled sub dags
Even if we use dag_run.run_type to figure out the run type, what is the right way to get execution_date for scheduled runs ?
execution_date will still work until 3.0. logical_date is the new variable name (the value is the same). But by the fact you’re using following_schedule, I suspect data_interval_end actually provides more suitable semantics for you use case.
But the calendar widget introduced in #16141 does not employ sub-second accurary, so the milliseconds become zero, and all manually-triggered runs now evaluate the expression to dag.following_schedule(execution_date).isoformat()
instead, which always fails because a manually triggered run never has a following schedule (both prior and after AIP-39).
also this reasoning doesn't hold true for this issue since the error is seen for scheduled sub dags
also this reasoning doesn't hold true for this issue since the error is seen for scheduled sub dags
Sorry I missed this. A sub-DAG’s execution date is inherited from its parent DAG, so the behaviour change in the parent DAG creeps down to its sub-DAGs.
Activity
boring-cyborg commentedon Nov 16, 2021
Thanks for opening your first issue here! Be sure to follow the issue template!
uranusjr commentedon Nov 16, 2021
Please provide a DAG for reproduction.
AnithaGG commentedon Nov 16, 2021
AnithaGG commentedon Nov 16, 2021
Any spark job would do to reproduce this issue since the DAG fails before triggering the job
potiuk commentedon Nov 16, 2021
What's spark_job() doing ? This must be something internal to your organisation?
AnithaGG commentedon Nov 16, 2021
It creates a SparkSubmitOperator. I have updated the example
potiuk commentedon Nov 16, 2021
OK. Now we need to know what
spark_args()does as well.AnithaGG commentedon Nov 16, 2021
ah mb.. it creates application_args (List[String]) from args and kwargs arguments. Something like this
AnithaGG commentedon Nov 16, 2021
On a related note, noticed from the airflow doc that {{ execution_date }} is deprecated.
I see that {{ ts }} is equivalent to execution_date isoformat but this is a string type. Do we have an equivalent for execution_date in datetime type ?
potiuk commentedon Nov 16, 2021
OK. Seems prety legit then. Hmm. Interesting - we converted the
execution_dateto be lazy-proxy object so essentially it should work in a backwards compatible way (correct @uranusjr ?).I wonder what happen if you replace the execution_date with
dag_run.logical_date- see deprecated values here https://airflow.apache.org/docs/apache-airflow/stable/templates-ref.html?highlight=macros#variables - but it should work regardless, so it would be great to track this one down.potiuk commentedon Nov 16, 2021
I believe I know what the problem is. yes the
dag_run.logical_dateshould work and I know what the problem is: @uranusjr - this is the "coerce_date" in following_schedule:Here is
coerce_date:uranusjr commentedon Nov 16, 2021
I can see why the error happens, but not why the error is wrong. A
@onceschedule does not have a following schedule (because it runs only once?), sodag.following_schedule(execution_date)is correct to returnNone. This also should be the case if you usedag_run.logical_date.potiuk commentedon Nov 16, 2021
Ah. I see the
@onceindeed you arre right.11 remaining items
AnithaGG commentedon Nov 16, 2021
If schedule_interval is a valid case, is it coerce_date issue ?
Could you confirm if dag_run.logical_date would work for this ?
AnithaGG commentedon Nov 16, 2021
When the DAGs are manually triggered(after a couple of runs), it renders the date. But in scheduled runs, it always fails returning None
potiuk commentedon Nov 16, 2021
Can you try it pleaase? I cannot "confirm it" but if it works that will give us a clue to look further
AnithaGG commentedon Nov 16, 2021
dag_run.logical_date didn't work either


AnithaGG commentedon Nov 17, 2021
Narrowing down further,
dag.following_schedule(execution_date)returns null for scheduled sub dags only. Main dags return valueuranusjr commentedon Nov 17, 2021
Oh I know what’s going on now. We’ve been looking at entirely wrong places.
The change that actually broke the logic is #16141. Previously, a manually-triggered run always has its execution date set to
timezone.now(), which has an approximately 99.9999% chance to return a non-zero millisecond, sois (almost) always evaluated to
which works.
But the calendar widget introduced in #16141 does not employ sub-second accurary, so the milliseconds become zero, and all manually-triggered runs now evaluate the expression to
instead, which always fails because a manually triggered run never has a following schedule (both prior and after AIP-39).
Ultimately, using the millisecond field to distinguish between scheduled and manual runs is a poor implementation in the first place. There’s still a 0.0001% chance the detection would fail, even if Airflow never broke that. It is both more semantically correct and reliable to use
dag_run.run_typeinstead.I think the best we can do here is to add an entry to UPDATING.md to describe this behavioural change.
AnithaGG commentedon Nov 17, 2021
One correction.
(execution_date if execution_date.microsecond > 0 else dag.following_schedule(execution_date)).isoformat()fails for scheduled runs as well.
dag.following_schedule(execution_date)returns null for scheduled sub dagsEven if we use
dag_run.run_typeto figure out the run type, what is the right way to get execution_date for scheduled runs ?uranusjr commentedon Nov 17, 2021
execution_datewill still work until 3.0.logical_dateis the new variable name (the value is the same). But by the fact you’re usingfollowing_schedule, I suspectdata_interval_endactually provides more suitable semantics for you use case.AnithaGG commentedon Nov 17, 2021
also this reasoning doesn't hold true for this issue since the error is seen for scheduled sub dags
eladkal commentedon Jun 13, 2022
@uranusjr was this added? seems like there is no actual task on this issue other than clarification?
uranusjr commentedon Jun 13, 2022
No I don’t think so. I’ll do that.
Sorry I missed this. A sub-DAG’s execution date is inherited from its parent DAG, so the behaviour change in the parent DAG creeps down to its sub-DAGs.