-
Notifications
You must be signed in to change notification settings - Fork 16.4k
[AIRFLOW-149] First draft of blocked TI dependency explainer #1525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Dag hash function tried (and failed) to hash the list of tasks, then fell back on repr-ing the list, which took forever. Instead, hash tuple(task_dict.keys()). In addition this replaces two slow list comprehensions with much faster hash lookups (using the new task_dict).
If a process places items in a queue and the process is joined before the queue is emptied, it can lead to a deadlock under some circumstances. Closes AIRFLOW-61. See for example: https://docs.python.org/3/library/multiprocessing.html#all-start-methods ("Joining processes that use queues") http://stackoverflow.com/questions/31665328/python-3-multiprocessing-queue-deadlock-when-calling-join-before-the-queue-is-em http://stackoverflow.com/questions/31708646/process-join-and-queue-dont-work-with-large-numbers http://stackoverflow.com/questions/19071529/python-multiprocessing-125-list-never-finishes
When Scheduler is run with `—num-runs`, there can be multiple Schedulers and Executors all trying to run tasks. For queued tasks, Scheduler was previously only trying to run tasks that it itself had queued — but that doesn’t work if the Scheduler is restarting. This PR reverts that behavior and adds two types of “best effort” executions — before running a TI, executors check if it is already running, and before ending executors call sync() one last time
The scheduler can encounter a queued task twice before the task actually starts to run -- this locks the task and avoids that condition.
|
|
|
@aoen Firstly, thanks for this great work. A couple of things:. Paused DAG checking now occurs at the task instance level in models.py instead of in jobs.py to consolidate the "should this task run" logic into one place I think this should happen at the DagRun level not at the task instance level. (Dag->DagRun Task->TaskInstance) To make things less confusing for users, force running a task will now override/ignore: This is an operational issue. At a minimum I would like to see a "Pool is full. Please confirm" task instance is already queued What will happen with the queued instance? Failed tasks will no longer be considered "runnable", they must be force-run or cleared first from the UI to be run, just like successful tasks Nice! Future work Parallelize task instance dependency checking. I have some ideas about this. I think we can go below 1-2s loops on a single core and reducing the amount of queries significantly by having taskinstances update counters in the dagrun. The issue lies in the are_dependencies_met function that does aggregate queries for every task(!). |
airflow/jobs.py
Outdated
| State.RUNNING, State.QUEUED, State.SUCCESS, State.FAILED): | ||
| continue | ||
| elif ti.is_runnable(flag_upstream_failed=True): | ||
| elif ti.are_dependencies_met(flag_upstream_failed=True): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please provide the session if not necessary to have a new session. It reduces connection overhead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do in final version (still a WIP).
|
Sorry, missed your comments. Thanks much for the review!
I don't think it's that bad to check at both places, i.e. I think it is logical to not schedule any new tasks once a DAG is paused, even for existing dagruns. My medium-term vision is something like:
The way I have it currently is that the UIs (web UI, CLI backfill etc) will throw an error if you don't specify "force". The UIs won't prompt you if you are running with "force" which I think is acceptable since a user must actively click/add "force". I believe backfilling a task ignores the pool at the moment anyways by default so my change just makes starting a task in the the web UI consistent with backfilling. That being said if you feel strongly about this let me know, I think the simpler solution for now would be to just not allow overriding pools. Kind of a tangent but I think the primary use case of pools is to simulate a throttling layer to external services. I can see the usefulness of this in practice and understand that users are depending on this functionality in the wild, but I think the throttling should really be performed in the respective services (separation of concerns, control over what "quota" means, ability to throttle from sources other than Airflow, etc), not in Airflow. I think pools should eventually be removed as they add a lot of complexity both to users and in the code. I'm curious what you and others think about this.
The executors check if another executor already picked up the instance (i.e. if the TI's state == RUNNING), so double-queueing shouldn't be a problem. The DB state will be consistent too although the previous queueing attempt would be lost since we don't have a concept of a TI state history at the moment. Queuing a task should also be an idempotent operation.
Agreed, I liked the idea of are_dependencies_met doing aggregate queries using in-process caching and prefetching a whole bunch of data to reduce requests to the DB. For example we could query the task instances for all of the upstream/downstream task instances in the same request. The nice thing is we would have separation of concerns, i.e. accessing the queried data would be done the same way everywhere and we wouldn't need to pass around the DB query results between functions. I haven't thought about this too deeply though the complexity might not be worth it. |
9682261 to
0a9158b
Compare
2a2c256 to
2bf3d4e
Compare
Instead of parsing the DAG definition files in the same process as the scheduler, this change parses the files in a child process. This helps to isolate the scheduler from bad user code.
|
Great work, do we have a timeline for merging this PR? #1579 is trying to resolve some concurrency bug, which you believe is already covered by this PR. |
|
Should be merged fairly shortly after #1559 There were 3 big commits (including this one) that touched the core that stepped on each other toes. |
Some iterating Some iterating Some iterating Some iterating Some iterating Some iterating Some iterating [airflow] 2nd attempt at releasing airflow 1.7.1 Accidentally worked in airflow dir instead of airflow-dev, merged in changes iterating iterating iterating CHECKPOINT BEFORE REBASE
|
The final PR (minus rebasing against master) is here: aoen#1 , closing this PR |
See #1435 for previous discussion for this PR
THIS IS A DRAFT WIP PULL REQUEST
This is a draft for a solution to #1383 among several other things.
There are tons of things broken/missing here, I'm just putting this out there to get some initial eyes on the high-level changes.
Goals
e.g. (this will not be the final product):
Things to review in this PR
Things NOT to review in this PR
Some big changes made in this PR
This will break one use case:
Using pools to restrict some resource on airflow executors themselves (rather than an external resource like a DB), e.g. some task uses 60% of cpu on a worker so we restrict that task's pool size to 1 to prevent two of the tasks from running on the same host. When backfilling a task of this type, now the backfill will wait on the pool to have slots open up before running the task even though we don't need to do this if backfilling on a different host outside of the pool. I think breaking this use case is OK since the use case is a hack due to not having a proper resource isolation solution (e.g. mesos should be used in this case instead).
Future Work (will not be released in the first version)
Tests for dependency contexts are missing, I will add them once I get an an initial LGTM
@mistercrunch @jlowin @plypaul