fix(oauth): add thread-safe token refresh with double-checked locking#883
fix(oauth): add thread-safe token refresh with double-checked locking#883
Conversation
When multiple streams start concurrently with an expired token, they can all detect the token is expired and attempt to refresh simultaneously. For single-use refresh tokens, this causes failures because the refresh token is invalidated after the first successful refresh. This fix adds a class-level lock to AbstractOauth2Authenticator that serializes token refresh attempts. The double-checked locking pattern ensures that: 1. Only one thread performs the actual refresh 2. Other threads wait and use the newly refreshed token 3. No redundant refresh attempts occur Changes: - Add _token_refresh_lock class-level threading.Lock to AbstractOauth2Authenticator - Update get_access_token() in AbstractOauth2Authenticator to use double-checked locking - Update get_access_token() in SingleUseRefreshTokenOauth2Authenticator with same pattern - Add unit tests for concurrent token refresh scenarios Co-Authored-By: gl_anatolii.yatsuk@airbyte.io <gl_anatolii.yatsuk@airbyte.io>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
👋 Greetings, Airbyte Team Member!Here are some helpful tips and reminders for your convenience. 💡 Show Tips and TricksTesting This CDK VersionYou can test this version of the CDK using the following: # Run the CLI from this branch:
uvx 'git+https://github.com/airbytehq/airbyte-python-cdk.git@devin/1769515185-thread-safe-oauth-refresh#egg=airbyte-python-cdk[dev]' --help
# Update a connector to use the CDK from this branch ref:
cd airbyte-integrations/connectors/source-example
poe use-cdk-branch devin/1769515185-thread-safe-oauth-refreshPR Slash CommandsAirbyte Maintainers can execute the following slash commands on your PR:
|
There was a problem hiding this comment.
Pull request overview
This PR fixes a race condition in OAuth token refresh by implementing thread-safe double-checked locking. When multiple streams start concurrently with an expired token, they would all attempt to refresh simultaneously, causing failures with single-use refresh tokens since the token is invalidated after the first successful refresh.
Changes:
- Added a class-level
threading.LocktoAbstractOauth2Authenticatorto synchronize token refresh across all instances - Implemented double-checked locking pattern in both
AbstractOauth2Authenticator.get_access_token()andSingleUseRefreshTokenOauth2Authenticator.get_access_token() - Added comprehensive unit tests verifying that 5 concurrent threads result in exactly 1 refresh call
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py | Added class-level lock and double-checked locking in base authenticator |
| airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py | Added double-checked locking to SingleUseRefreshTokenOauth2Authenticator |
| unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py | Added concurrent token refresh tests for both authenticator types |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py
Show resolved
Hide resolved
unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py
Show resolved
Hide resolved
|
/prerelease
|
aldogonzalez8
left a comment
There was a problem hiding this comment.
APPROVED
Just left a comment about PEP8.
unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py
Outdated
Show resolved
Hide resolved
unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py
Outdated
Show resolved
Hide resolved
airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py
Outdated
Show resolved
Hide resolved
Address PR review feedback from aldogonzalez8 to follow PEP8 import conventions. Co-Authored-By: gl_anatolii.yatsuk@airbyte.io <gl_anatolii.yatsuk@airbyte.io>
…e conflicts Co-Authored-By: gl_anatolii.yatsuk@airbyte.io <gl_anatolii.yatsuk@airbyte.io>
Summary
Fixes a race condition where multiple streams starting concurrently with an expired OAuth token would all attempt to refresh simultaneously. For single-use refresh tokens, this causes failures because the refresh token is invalidated after the first successful refresh.
The fix adds a class-level
threading.LocktoAbstractOauth2Authenticatorand implements double-checked locking inget_access_token():The same pattern is applied to
SingleUseRefreshTokenOauth2Authenticator.get_access_token()which overrides the base method.Review & Testing Checklist for Human
refresh_access_token(). Test with an actual connector that has multiple streams and uses single-use refresh tokens (e.g., a connector where you've observed this issue).refresh_access_token()directly, bypassing the lock protection.Notes
Requested by: gl_anatolii.yatsuk@airbyte.io
Link to Devin run: https://app.devin.ai/sessions/47c89308cb7f48b48d6bb424a393166e