This is an automated email from the ASF dual-hosted git repository.
pierrejeambrun pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-1-test by this push:
new 6fc7177a1fe Revert "[v3-1-test] Fix race condition in auth manager
initialization (#62214…" (#62407)
6fc7177a1fe is described below
commit 6fc7177a1fef395b4bdb0f6cc06a4a6884101190
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Tue Feb 24 13:58:06 2026 +0100
Revert "[v3-1-test] Fix race condition in auth manager initialization
(#62214…" (#62407)
This reverts commit 5605d32a9865f1d13076c493b7f26a1bef27df0a.
---
airflow-core/src/airflow/api_fastapi/app.py | 34 ++++++++++-----------
airflow-core/tests/unit/api_fastapi/test_app.py | 35 ----------------------
airflow-core/tests/unit/utils/test_db.py | 3 --
.../fab/auth_manager/cli_commands/utils.py | 3 --
.../unit/fab/auth_manager/api_fastapi/conftest.py | 2 --
.../fab/tests/unit/fab/auth_manager/conftest.py | 5 ----
.../unit/fab/auth_manager/test_fab_auth_manager.py | 6 ----
.../tests/unit/fab/auth_manager/test_security.py | 3 --
.../fab/auth_manager/views/test_permissions.py | 3 --
.../unit/fab/auth_manager/views/test_roles_list.py | 3 --
.../tests/unit/fab/auth_manager/views/test_user.py | 3 --
.../unit/fab/auth_manager/views/test_user_edit.py | 3 --
.../unit/fab/auth_manager/views/test_user_stats.py | 3 --
providers/fab/tests/unit/fab/www/test_auth.py | 3 --
.../fab/www/views/test_views_custom_user_views.py | 28 -----------------
.../common/auth_backend/test_google_openid.py | 7 -----
.../unit/keycloak/auth_manager/routes/conftest.py | 3 +-
17 files changed, 16 insertions(+), 131 deletions(-)
diff --git a/airflow-core/src/airflow/api_fastapi/app.py
b/airflow-core/src/airflow/api_fastapi/app.py
index 02b27a9b5f1..7c05295807e 100644
--- a/airflow-core/src/airflow/api_fastapi/app.py
+++ b/airflow-core/src/airflow/api_fastapi/app.py
@@ -17,9 +17,7 @@
from __future__ import annotations
import logging
-import threading
from contextlib import AsyncExitStack, asynccontextmanager
-from functools import cache
from typing import TYPE_CHECKING, cast
from urllib.parse import urlsplit
@@ -56,10 +54,8 @@ RESERVED_URL_PREFIXES = ["/api/v2", "/ui", "/execution"]
log = logging.getLogger(__name__)
-
-class _AuthManagerState:
- instance: BaseAuthManager | None = None
- _lock = threading.Lock()
+app: FastAPI | None = None
+auth_manager: BaseAuthManager | None = None
@asynccontextmanager
@@ -111,16 +107,19 @@ def create_app(apps: str = "all") -> FastAPI:
return app
-@cache
def cached_app(config=None, testing=False, apps="all") -> FastAPI:
"""Return cached instance of Airflow API app."""
- return create_app(apps=apps)
+ global app
+ if not app:
+ app = create_app(apps=apps)
+ return app
def purge_cached_app() -> None:
"""Remove the cached version of the app and auth_manager in global
state."""
- cached_app.cache_clear()
- _AuthManagerState.instance = None
+ global app, auth_manager
+ app = None
+ auth_manager = None
def get_auth_manager_cls() -> type[BaseAuthManager]:
@@ -141,13 +140,10 @@ def get_auth_manager_cls() -> type[BaseAuthManager]:
def create_auth_manager() -> BaseAuthManager:
"""Create the auth manager."""
- if _AuthManagerState.instance is not None:
- return _AuthManagerState.instance
- with _AuthManagerState._lock:
- if _AuthManagerState.instance is None:
- auth_manager_cls = get_auth_manager_cls()
- _AuthManagerState.instance = auth_manager_cls()
- return _AuthManagerState.instance
+ global auth_manager
+ auth_manager_cls = get_auth_manager_cls()
+ auth_manager = auth_manager_cls()
+ return auth_manager
def init_auth_manager(app: FastAPI | None = None) -> BaseAuthManager:
@@ -165,12 +161,12 @@ def init_auth_manager(app: FastAPI | None = None) ->
BaseAuthManager:
def get_auth_manager() -> BaseAuthManager:
"""Return the auth manager, provided it's been initialized before."""
- if _AuthManagerState.instance is None:
+ if auth_manager is None:
raise RuntimeError(
"Auth Manager has not been initialized yet. "
"The `init_auth_manager` method needs to be called first."
)
- return _AuthManagerState.instance
+ return auth_manager
def init_plugins(app: FastAPI) -> None:
diff --git a/airflow-core/tests/unit/api_fastapi/test_app.py
b/airflow-core/tests/unit/api_fastapi/test_app.py
index 3ad7437c373..448d527ab6b 100644
--- a/airflow-core/tests/unit/api_fastapi/test_app.py
+++ b/airflow-core/tests/unit/api_fastapi/test_app.py
@@ -16,7 +16,6 @@
# under the License.
from __future__ import annotations
-import threading
from unittest import mock
import pytest
@@ -119,37 +118,3 @@ def test_plugin_with_invalid_url_prefix(caplog,
fastapi_apps, expected_message,
assert any(expected_message in rec.message for rec in caplog.records)
assert not any(r.path == invalid_path for r in app.routes)
-
-
-def test_create_auth_manager_thread_safety():
- """Concurrent calls to create_auth_manager must return the same singleton
instance."""
- call_count = 0
- singleton = None
-
- class FakeAuthManager:
- def __init__(self):
- nonlocal call_count, singleton
- call_count += 1
- singleton = self
-
- app_module.purge_cached_app()
-
- results = []
- barrier = threading.Barrier(10)
-
- def call_create_auth_manager():
- barrier.wait()
- results.append(app_module.create_auth_manager())
-
- with mock.patch.object(app_module, "get_auth_manager_cls",
return_value=FakeAuthManager):
- threads = [threading.Thread(target=call_create_auth_manager) for _ in
range(10)]
- for t in threads:
- t.start()
- for t in threads:
- t.join()
-
- assert len(results) == 10
- assert all(r is singleton for r in results)
- assert call_count == 1
-
- app_module.purge_cached_app()
diff --git a/airflow-core/tests/unit/utils/test_db.py
b/airflow-core/tests/unit/utils/test_db.py
index 1b9e3062b90..d6a8d4c1d66 100644
--- a/airflow-core/tests/unit/utils/test_db.py
+++ b/airflow-core/tests/unit/utils/test_db.py
@@ -240,9 +240,6 @@ class TestDb:
mock_upgrade = mocker.patch("alembic.command.upgrade")
- from airflow.api_fastapi.app import purge_cached_app
-
- purge_cached_app()
with conf_vars(auth):
upgradedb()
diff --git
a/providers/fab/src/airflow/providers/fab/auth_manager/cli_commands/utils.py
b/providers/fab/src/airflow/providers/fab/auth_manager/cli_commands/utils.py
index f5e5ea9c790..174b3867ad0 100644
--- a/providers/fab/src/airflow/providers/fab/auth_manager/cli_commands/utils.py
+++ b/providers/fab/src/airflow/providers/fab/auth_manager/cli_commands/utils.py
@@ -28,7 +28,6 @@ from flask import Flask
from sqlalchemy.engine import make_url
import airflow
-from airflow.api_fastapi.app import purge_cached_app
from airflow.configuration import conf
from airflow.exceptions import AirflowConfigException
from airflow.providers.fab.www.extensions.init_appbuilder import
init_appbuilder
@@ -50,8 +49,6 @@ def _return_appbuilder(app: Flask) -> AirflowAppBuilder:
@contextmanager
def get_application_builder() -> Generator[AirflowAppBuilder, None, None]:
- _return_appbuilder.cache_clear()
- purge_cached_app()
static_folder = os.path.join(os.path.dirname(airflow.__file__), "www",
"static")
flask_app = Flask(__name__, static_folder=static_folder)
webserver_config = conf.get_mandatory_value("fab", "config_file")
diff --git a/providers/fab/tests/unit/fab/auth_manager/api_fastapi/conftest.py
b/providers/fab/tests/unit/fab/auth_manager/api_fastapi/conftest.py
index 4c7f566d395..86273a0af74 100644
--- a/providers/fab/tests/unit/fab/auth_manager/api_fastapi/conftest.py
+++ b/providers/fab/tests/unit/fab/auth_manager/api_fastapi/conftest.py
@@ -19,13 +19,11 @@ from __future__ import annotations
import pytest
from fastapi.testclient import TestClient
-from airflow.api_fastapi.app import purge_cached_app
from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
@pytest.fixture(scope="module")
def fab_auth_manager():
- purge_cached_app()
return FabAuthManager(None)
diff --git a/providers/fab/tests/unit/fab/auth_manager/conftest.py
b/providers/fab/tests/unit/fab/auth_manager/conftest.py
index 0def443a7f3..2cad4b4db03 100644
--- a/providers/fab/tests/unit/fab/auth_manager/conftest.py
+++ b/providers/fab/tests/unit/fab/auth_manager/conftest.py
@@ -22,9 +22,7 @@ from pathlib import Path
import pytest
-from airflow.api_fastapi.app import purge_cached_app
from airflow.providers.fab.www import app
-from airflow.providers.fab.www.app import purge_cached_app as
purge_fab_cached_app
from tests_common.test_utils.config import conf_vars
from unit.fab.decorators import dont_initialize_flask_app_submodules
@@ -32,9 +30,6 @@ from unit.fab.decorators import
dont_initialize_flask_app_submodules
@pytest.fixture(scope="session")
def minimal_app_for_auth_api():
- purge_cached_app()
- purge_fab_cached_app()
-
@dont_initialize_flask_app_submodules(
skip_all_except=[
"init_appbuilder",
diff --git a/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
b/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
index a60ed124664..85fb85dace9 100644
--- a/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
+++ b/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
@@ -159,12 +159,6 @@ def auth_manager():
@pytest.fixture
def flask_app():
- from airflow.api_fastapi.app import purge_cached_app
-
- purge_cached_app()
- from airflow.providers.fab.www.app import purge_cached_app as
purge_fab_cached_app
-
- purge_fab_cached_app()
with conf_vars(
{
(
diff --git a/providers/fab/tests/unit/fab/auth_manager/test_security.py
b/providers/fab/tests/unit/fab/auth_manager/test_security.py
index 218677098c1..75dec27d8b2 100644
--- a/providers/fab/tests/unit/fab/auth_manager/test_security.py
+++ b/providers/fab/tests/unit/fab/auth_manager/test_security.py
@@ -203,9 +203,6 @@ def clear_db_before_test():
@pytest.fixture(scope="module")
def app():
- from airflow.api_fastapi.app import purge_cached_app
-
- purge_cached_app()
with conf_vars(
{
(
diff --git
a/providers/fab/tests/unit/fab/auth_manager/views/test_permissions.py
b/providers/fab/tests/unit/fab/auth_manager/views/test_permissions.py
index c33383e856d..52c67f57425 100644
--- a/providers/fab/tests/unit/fab/auth_manager/views/test_permissions.py
+++ b/providers/fab/tests/unit/fab/auth_manager/views/test_permissions.py
@@ -30,9 +30,6 @@ from unit.fab.utils import client_with_login
@pytest.fixture(scope="module")
def fab_app():
- from airflow.api_fastapi.app import purge_cached_app
-
- purge_cached_app()
with conf_vars(
{
(
diff --git a/providers/fab/tests/unit/fab/auth_manager/views/test_roles_list.py
b/providers/fab/tests/unit/fab/auth_manager/views/test_roles_list.py
index 7f293aa85e2..4ef28203ef7 100644
--- a/providers/fab/tests/unit/fab/auth_manager/views/test_roles_list.py
+++ b/providers/fab/tests/unit/fab/auth_manager/views/test_roles_list.py
@@ -30,9 +30,6 @@ from unit.fab.utils import client_with_login
@pytest.fixture(scope="module")
def fab_app():
- from airflow.api_fastapi.app import purge_cached_app
-
- purge_cached_app()
with conf_vars(
{
(
diff --git a/providers/fab/tests/unit/fab/auth_manager/views/test_user.py
b/providers/fab/tests/unit/fab/auth_manager/views/test_user.py
index a8748b14d92..c56d5f7aa09 100644
--- a/providers/fab/tests/unit/fab/auth_manager/views/test_user.py
+++ b/providers/fab/tests/unit/fab/auth_manager/views/test_user.py
@@ -30,9 +30,6 @@ from unit.fab.utils import client_with_login
@pytest.fixture(scope="module")
def fab_app():
- from airflow.api_fastapi.app import purge_cached_app
-
- purge_cached_app()
with conf_vars(
{
(
diff --git a/providers/fab/tests/unit/fab/auth_manager/views/test_user_edit.py
b/providers/fab/tests/unit/fab/auth_manager/views/test_user_edit.py
index 9b34aab2bb0..97e57d4fe28 100644
--- a/providers/fab/tests/unit/fab/auth_manager/views/test_user_edit.py
+++ b/providers/fab/tests/unit/fab/auth_manager/views/test_user_edit.py
@@ -30,9 +30,6 @@ from unit.fab.utils import client_with_login
@pytest.fixture(scope="module")
def fab_app():
- from airflow.api_fastapi.app import purge_cached_app
-
- purge_cached_app()
with conf_vars(
{
(
diff --git a/providers/fab/tests/unit/fab/auth_manager/views/test_user_stats.py
b/providers/fab/tests/unit/fab/auth_manager/views/test_user_stats.py
index 6ebbf614c70..3671217e0fe 100644
--- a/providers/fab/tests/unit/fab/auth_manager/views/test_user_stats.py
+++ b/providers/fab/tests/unit/fab/auth_manager/views/test_user_stats.py
@@ -30,9 +30,6 @@ from unit.fab.utils import client_with_login
@pytest.fixture(scope="module")
def fab_app():
- from airflow.api_fastapi.app import purge_cached_app
-
- purge_cached_app()
with conf_vars(
{
(
diff --git a/providers/fab/tests/unit/fab/www/test_auth.py
b/providers/fab/tests/unit/fab/www/test_auth.py
index 7559ecbb704..b20ee27bd34 100644
--- a/providers/fab/tests/unit/fab/www/test_auth.py
+++ b/providers/fab/tests/unit/fab/www/test_auth.py
@@ -33,9 +33,6 @@ mock_call = Mock()
@pytest.fixture
def app():
- from airflow.api_fastapi.app import purge_cached_app
-
- purge_cached_app()
with conf_vars(
{
(
diff --git
a/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py
b/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py
index 9cafe982476..e63ce584e3b 100644
--- a/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py
+++ b/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py
@@ -63,34 +63,6 @@ PERMISSIONS_TESTS_PARAMS = [
]
-def delete_roles(app):
- for role_name in ["role_edit_one_dag"]:
- delete_role(app, role_name)
-
-
[email protected]
-def app():
- from airflow.api_fastapi.app import purge_cached_app
-
- purge_cached_app()
- with conf_vars(
- {
- (
- "core",
- "auth_manager",
- ):
"airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager",
- }
- ):
- app = application.create_app(enable_plugins=False)
- app.config["WTF_CSRF_ENABLED"] = False
- yield app
-
-
[email protected]
-def client(app):
- return app.test_client()
-
-
class TestSecurity:
@classmethod
def setup_class(cls):
diff --git
a/providers/google/tests/unit/google/common/auth_backend/test_google_openid.py
b/providers/google/tests/unit/google/common/auth_backend/test_google_openid.py
index 16ac6cc6ac0..4cc6ad386c0 100644
---
a/providers/google/tests/unit/google/common/auth_backend/test_google_openid.py
+++
b/providers/google/tests/unit/google/common/auth_backend/test_google_openid.py
@@ -34,8 +34,6 @@ if not AIRFLOW_V_3_0_PLUS:
allow_module_level=True,
)
-from airflow.api_fastapi.app import purge_cached_app
-
from tests_common.test_utils.config import conf_vars
@@ -44,11 +42,6 @@ def google_openid_app():
if importlib.util.find_spec("flask_session") is None:
return None
- purge_cached_app()
- from airflow.providers.fab.www.app import purge_cached_app as
purge_fab_cached_app
-
- purge_fab_cached_app()
-
def factory():
with conf_vars(
{
diff --git
a/providers/keycloak/tests/unit/keycloak/auth_manager/routes/conftest.py
b/providers/keycloak/tests/unit/keycloak/auth_manager/routes/conftest.py
index bd6a40eb62a..1807778b86a 100644
--- a/providers/keycloak/tests/unit/keycloak/auth_manager/routes/conftest.py
+++ b/providers/keycloak/tests/unit/keycloak/auth_manager/routes/conftest.py
@@ -19,7 +19,7 @@ from __future__ import annotations
import pytest
from fastapi.testclient import TestClient
-from airflow.api_fastapi.app import create_app, purge_cached_app
+from airflow.api_fastapi.app import create_app
from airflow.providers.keycloak.auth_manager.constants import (
CONF_CLIENT_ID_KEY,
CONF_CLIENT_SECRET_KEY,
@@ -32,7 +32,6 @@ from tests_common.test_utils.config import conf_vars
@pytest.fixture
def client():
- purge_cached_app()
with conf_vars(
{
(