You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by be...@apache.org on 2026/04/20 22:08:41 UTC

(superset) branch master updated: fix(oauth2): silence lock acquisition errors on token refresh (#39463)

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

betodealmeida pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 5fb89b865da fix(oauth2): silence lock acquisition errors on token refresh (#39463)
5fb89b865da is described below

commit 5fb89b865da5ce2f5736a84e9de41a2d33e2ade4
Author: Beto Dealmeida <ro...@dealmeida.net>
AuthorDate: Mon Apr 20 18:08:33 2026 -0400

    fix(oauth2): silence lock acquisition errors on token refresh (#39463)
    
    Co-authored-by: Beto Dealmeida <be...@preset.io>
---
 superset/utils/oauth2.py               |  2 ++
 tests/unit_tests/utils/oauth2_tests.py | 39 ++++++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+)

diff --git a/superset/utils/oauth2.py b/superset/utils/oauth2.py
index 76b671b4109..62e6559a1b6 100644
--- a/superset/utils/oauth2.py
+++ b/superset/utils/oauth2.py
@@ -82,6 +82,8 @@ def generate_code_challenge(code_verifier: str) -> str:
     factor=10,
     base=2,
     max_tries=5,
+    raise_on_giveup=False,
+    giveup_log_level=logging.DEBUG,
 )
 def get_oauth2_access_token(
     config: OAuth2ClientConfig,
diff --git a/tests/unit_tests/utils/oauth2_tests.py b/tests/unit_tests/utils/oauth2_tests.py
index a320a8a23e8..1ca9a4eb21b 100644
--- a/tests/unit_tests/utils/oauth2_tests.py
+++ b/tests/unit_tests/utils/oauth2_tests.py
@@ -338,6 +338,45 @@ def test_encode_decode_oauth2_state(
     assert decoded["user_id"] == 2
 
 
+def test_get_oauth2_access_token_lock_not_acquired_no_error_log(
+    mocker: MockerFixture,
+    caplog: pytest.LogCaptureFixture,
+) -> None:
+    """
+    Test that when a distributed lock can't be acquired, no error is logged and
+    the function returns None instead of raising.
+
+    This scenario occurs when a dashboard with multiple charts from the same
+    OAuth2-enabled DB has an expired token: simultaneous requests compete for
+    the lock, and only the first one wins. The rest should silently return None.
+    """
+    import logging
+
+    from superset.exceptions import AcquireDistributedLockFailedException
+
+    mocker.patch("time.sleep")  # avoid backoff delays in tests
+
+    db = mocker.patch("superset.utils.oauth2.db")
+    db_engine_spec = mocker.MagicMock()
+    token = mocker.MagicMock()
+    token.access_token = "access-token"  # noqa: S105
+    token.access_token_expiration = datetime(2024, 1, 1)
+    token.refresh_token = "refresh-token"  # noqa: S105
+    db.session.query().filter_by().one_or_none.return_value = token
+
+    mocker.patch(
+        "superset.utils.oauth2.refresh_oauth2_token",
+        side_effect=AcquireDistributedLockFailedException("Lock not available"),
+    )
+
+    with freeze_time("2024-01-02"):
+        with caplog.at_level(logging.DEBUG):
+            result = get_oauth2_access_token({}, 1, 1, db_engine_spec)
+
+    assert result is None
+    assert not any(record.levelno >= logging.ERROR for record in caplog.records)
+
+
 def test_get_oauth2_redirect_uri_from_config(mocker: MockerFixture) -> None:
     """
     Test that get_oauth2_redirect_uri returns the configured value when set.