This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 583369271f5 Fix generate-providers-metadata hang by using spawn pools 
(#69763)
583369271f5 is described below

commit 583369271f5ae2098e9d800a0a427cf2eeb4edd4
Author: Jarek Potiuk <[email protected]>
AuthorDate: Sun Jul 12 20:29:54 2026 +0200

    Fix generate-providers-metadata hang by using spawn pools (#69763)
    
    The multiprocessing pools in the providers-metadata generation flow used the
    platform-default start method (fork on Linux). Before the pools are created 
the
    parent process has already used GitPython — which opens persistent
    `git cat-file --batch` subprocesses and is not fork-safe — and holds open
    network sockets from the version and constraints downloads. Forking that 
state
    into the workers left them with broken inherited file descriptors, so the 
pool
    deadlocked and the command hung forever, most reliably when
    --refresh-constraints-and-airflow-releases forces the parent through git 
and the
    network before forking.
    
    Switching these pools to the spawn start method gives each worker a clean
    interpreter with no inherited git subprocesses or sockets.
---
 .../src/airflow_breeze/commands/release_management_commands.py   | 9 ++++++---
 dev/breeze/src/airflow_breeze/utils/provider_dependencies.py     | 9 +++++++--
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git 
a/dev/breeze/src/airflow_breeze/commands/release_management_commands.py 
b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
index aed5d0e1ec9..5859b545e47 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
@@ -34,7 +34,7 @@ from copy import deepcopy
 from datetime import datetime
 from enum import Enum
 from functools import partial
-from multiprocessing import Pool
+from multiprocessing import get_context
 from pathlib import Path
 from subprocess import DEVNULL
 from typing import IO, TYPE_CHECKING, Any, Literal, NamedTuple
@@ -3611,7 +3611,10 @@ def generate_providers_metadata(
     )
 
     console_print("\n[info]Checking provider.yaml versions[1:] against PyPI 
for stale entries...[/]\n")
-    with Pool() as pypi_pool:
+    # "spawn" (not the platform-default fork): the parent has already used 
GitPython and
+    # opened network sockets before reaching here, and forking that state into 
workers
+    # deadlocks. See get_all_constraint_files_and_airflow_releases for the 
same reasoning.
+    with get_context("spawn").Pool() as pypi_pool:
         pruned_per_provider = 
pypi_pool.map(prune_unreleased_versions_from_provider_yaml, package_ids)
     total_pruned = 0
     for pid, pruned in zip(package_ids, pruned_per_provider):
@@ -3636,7 +3639,7 @@ def generate_providers_metadata(
         airflow_release_dates=airflow_release_dates,
         current_metadata=current_metadata,
     )
-    with Pool() as pool:
+    with get_context("spawn").Pool() as pool:
         results = pool.map(
             partial_generate_providers_metadata,
             package_ids,
diff --git a/dev/breeze/src/airflow_breeze/utils/provider_dependencies.py 
b/dev/breeze/src/airflow_breeze/utils/provider_dependencies.py
index a985a65b55d..d67e0ec9bfc 100644
--- a/dev/breeze/src/airflow_breeze/utils/provider_dependencies.py
+++ b/dev/breeze/src/airflow_breeze/utils/provider_dependencies.py
@@ -26,7 +26,7 @@ import urllib.error
 import urllib.request
 from collections.abc import Generator
 from functools import cache, partial
-from multiprocessing import Pool
+from multiprocessing import get_context
 from pathlib import Path
 from threading import Lock
 from typing import NamedTuple
@@ -276,7 +276,12 @@ def get_all_constraint_files_and_airflow_releases(
         
airflow_release_dates_path.write_text(json.dumps(airflow_release_dates, 
indent=2))
         console_print(f"[info]Airflow release dates saved in: 
{airflow_release_dates_path}[/]")
         with ci_group("Downloading constraints for all Airflow versions for 
all historical Python versions"):
-            with Pool() as pool:
+            # Use the "spawn" start method rather than the platform default: 
GitPython
+            # (used in the workers via get_tag_date) opens persistent `git 
cat-file --batch`
+            # subprocesses and is not fork-safe, and the parent already holds 
open network
+            # sockets from the version/constraints downloads. Forking that 
state into workers
+            # deadlocks; spawn gives each worker a clean interpreter.
+            with get_context("spawn").Pool() as pool:
                 # We use partial to pass the common parameters to the function
                 get_constraints_for_python_version_partial = partial(
                     get_constraints_for_python_version,

Reply via email to