Skip to content

Avoid large tuple IN query in SchedulerJobRunner._activate_referenced_assets on PostgreSQL (performance / perceived hanging) #61453

Closed

Description

@pjavier29

Apache Airflow version

3.1.6

If "Other Airflow 3 version" selected, which one?

3.1.5

What happened?

In SchedulerJobRunner._activate_referenced_assets(), the query that builds active_assets uses a tuple-based IN filter on (name, uri) constructed from a Python collection (assets). On PostgreSQL 17 with ~16,000 rows in the assets table, this pattern can generate a very large IN list (many bind parameters), which significantly increases parse/plan overhead and can make the scheduler appear to “hang” during asset orphanage/activation processing.

Repo: apache/airflow
File: airflow-core/src/airflow/jobs/scheduler_job_runner.py
Method: SchedulerJobRunner.activate_referenced_assets
DB engine: PostgreSQL 17
Scale: ~16,000 records in assets table (and the in-memory assets collection can be large)
Problematic pattern: tuple
(AssetActive.name, AssetActive.uri).in_((a.name, a.uri) for a in assets)

What you think should happen instead?

Fetching the subset of AssetActive corresponding to referenced assets should be efficient even when the referenced set is large, without constructing massive parameter lists from Python.

Use JOIN / EXISTS against a subquery/CTE representing the referenced assets set, so the set is computed inside the database and avoids Python-materialized parameter lists.
Conceptual approach: AssetActive JOIN (subselect of referenced assets) ON (name, uri).

How to reproduce

This issue is most visible when the referenced-assets set is large, since the current implementation materializes the set in Python and feeds it back to Postgres as a giant IN list.
Specific blo

Query specifies blocker:
SELECT airflow.asset_active.name, airflow.asset_active.uri
FROM airflow.asset_active
WHERE (airflow.asset_active.name, airflow.asset_active.uri) IN (('s3://bucket-airflow/prod/dataset/8495.txt', 's3://bucket-airflow/prod/dataset/8495.txt'), ('s3://bucket-airflow/prod/dataset/10421.txt', 's3://bucket-airflow/prod/dataset/10421.txt'), .......

This query block the scheduler and need reboot.

Operating System

Ubuntu 22.04.4 LTS

Versions of Apache Airflow Providers

No response

Deployment

Official Apache Airflow Helm Chart

Deployment details

No response

Anything else?

No response

Are you willing to submit PR?

  • Yes I am willing to submit a PR!

Code of Conduct

Activity

  1. boring-cyborg commented on Feb 4, 2026

    @boring-cyborg

    Thanks for opening your first issue here! Be sure to follow the issue template! If you are willing to raise PR to address this issue please do so, no need to wait for approval.

  2. wjddn279 commented on Feb 5, 2026

    @wjddn279
    Contributor

    Looks reasonable and interesting!
    I try to fix it.

  3. wjddn279 commented on Feb 6, 2026

    @wjddn279
    Contributor

    @pjavier29

    I've created a PR to fix the issue. Could you take a look? I'd appreciate your review!
    Also, please let me know if it can resolve your issue

  4. pjavier29 commented on Feb 6, 2026

    @pjavier29
    Author

    @wjddn279
    Are you missing the filter for the elements in the assets: Collection[AssetModel] list? Because right now you're doing a SELECT * on the entire AssetActive table, and new records might appear that you shouldn't be considering. Maybe you can filter in memory instead?

  5. removed
    needs-triagelabel for new issues that we didn't triage yet
    on Feb 7, 2026
  6. wjddn279 commented on Feb 9, 2026

    @wjddn279
    Contributor

    @pjavier29

    Yes, in my opinion, considering the usage scenario, I don't think additional filtering is necessary (refer to PR description), but in-memory filtering would be nice. However, I have a question. What was the main cause of the query performance degradation you observed?

    1. Network IO from passing long string queries (the items going into the IN operation)
    2. Query performance of the IN operation

    In case of 1, the same (or very similar) network IO would have occurred when calling https://github.com/apache/airflow/blob/main/airflow-core/src/airflow/jobs/scheduler_job_runner.py#L2992.

    If 2 is the problem, doing in-memory filtering in Python would be slower than the database if anything, not faster, wouldn't it?

  7. pjavier29 commented on Feb 9, 2026

    @pjavier29
    Author

    @wjddn279

    I don't think it's a network issue or a delay related to data volume. In my database, the asset_event table is 1.3GB and the asset table is 6.8MB (16,000 records).
    The query reaches the database, but I think it gets blocked because it's too long. If you check the database for locks with this query:

    SELECT
    l.pid,
    l.mode,
    l.granted,
    a.query,
    a.state,
    a.query_start
    FROM pg_locks l
    JOIN pg_stat_activity a ON l.pid = a.pid
    WHERE l.relation = 'asset_event'::regclass
    ORDER BY l.granted, l.pid;

    you'll see that the query arrives successfully but seems to get stuck there. You wait an hour and the record continues. The query never finishes.

    I don't think the in-memory filter is a problem because you already have those records in memory, and besides, filtering a list of 16000 records is not usually a problem for Python.

    Regards

  8. wjddn279 commented on Feb 9, 2026

    @wjddn279
    Contributor

    @pjavier29
    The query you posted in the issue doesn't look related to asset_event - did I miss something?

    SELECT airflow.asset_active.name, airflow.asset_active.uri
    FROM airflow.asset_active
    WHERE (airflow.asset_active.name, airflow.asset_active.uri) IN (('s3://bucket-airflow/prod/dataset/8495.txt', 's3://bucket-airflow/prod/dataset/8495.txt'), ('s3://bucket-airflow/prod/dataset/10421.txt', 's3://bucket-airflow/prod/dataset/10421.txt'), .......
    

    If you observed that asset_event is large and experiencing lock contention, then that query doesn't seem to be the bottleneck.

  9. Nataneljpwd commented on Feb 9, 2026

    @Nataneljpwd
    Contributor

    @pjavier29

    Yes, in my opinion, considering the usage scenario, I don't think additional filtering is necessary (refer to PR description), but in-memory filtering would be nice. However, I have a question. What was the main cause of the query performance degradation you observed?

    1. Network IO from passing long string queries (the items going into the IN operation)
    2. Query performance of the IN operation

    In case of 1, the same (or very similar) network IO would have occurred when calling https://github.com/apache/airflow/blob/main/airflow-core/src/airflow/jobs/scheduler_job_runner.py#L2992.

    If 2 is the problem, doing in-memory filtering in Python would be slower than the database if anything, not faster, wouldn't it?

    1. is not an issue compared to the amount of data you will get now without the filter, with a lot of assets it will be way worse than the current state

    2. not that bad, planning does not take a long time

    I think that either a proper query with a join (which is not that complex) is needed, or keep it as is and maybe paginate the request (though I think it will cause further performance degradation)

    In memory python filtering will be slow, and I say that from experience, where I tried to do in memory filtering in python of SQL objects, which was way slower than doing it in sql

  10. pjavier29 commented on Feb 10, 2026

    @pjavier29
    Author

    @wjddn279
    That query is what I used to understand why the other query ""SELECT airflow.asset_active.name, airflow.asset_active.uri
    FROM airflow.asset_activeWHERE (airflow.asset_active.name, airflow.asset_a"" was taking so long. That query shows you the locks on a specific table, in this case, the asset_active table.
    Sorry, in the previous message I used the wrong table, I put 'asset_event' but it should be 'asset_active'.

  11. Nataneljpwd commented on Feb 18, 2026

    @Nataneljpwd
    Contributor

    Hello, I have created a pull request addressing the issue using a join and a cte, this should solve the issue

  12. wjddn279 commented on Feb 18, 2026

    @wjddn279
    Contributor

    @choo121600 Could you please unassign me from this?

  13. pjavier29 commented on Feb 23, 2026

    @pjavier29
    Author

    @Nataneljpwd
    Hi,
    do you have the link to the PR?
    Regards

  14. Nataneljpwd commented on Feb 23, 2026

    @Nataneljpwd
    Contributor

    @Nataneljpwd
    Hi,
    do you have the link to the PR?
    Regards

    Sure, here you go

    #62114

    It is still a draft as some tests need to be fixed, hopefully I will get to it today

  15. Nataneljpwd commented on Feb 24, 2026

    @Nataneljpwd
    Contributor

    Hello, the pr is now ready for review, I need to fix a few mypy issues, other than that, everything is ready

  16. Nataneljpwd commented on Feb 24, 2026

    @Nataneljpwd
    Contributor

    Fixed the mypy issues, I would appreciate a review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions