Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dataconnect: DataConnectCredentialsTokenManager: initialize synchronously to fix race condition #6448

Conversation

dconeybe
Copy link
Contributor

@dconeybe dconeybe commented Nov 8, 2024

This PR changes the initialization of Data Conect's "Auth" and "AppCheck" mechanisms to be more synchronous. Not only does this fix a bug introduced a few commits ago by #6446 but also simplifies the logic and reduces the amount of non-determinism.

The race condition fixed by this PR manifested only on on my MacBook laptop (not on my Linux workstation) and is simply due to arbitrary thread scheduling differences. One such test failure looked like this:

failed: collect_gets_notified_of_per_data_deserializer_successes(com.google.firebase.dataconnect.QuerySubscriptionIntegrationTest)
java.lang.IllegalStateException: getToken() cannot be called before initialize()
	at com.google.firebase.dataconnect.core.DataConnectCredentialsTokenManager.getToken(DataConnectCredentialsTokenManager.kt:322)
	at com.google.firebase.dataconnect.core.DataConnectGrpcMetadata.get(DataConnectGrpcMetadata.kt:80)
	at com.google.firebase.dataconnect.core.DataConnectGrpcRPCs.executeMutation(DataConnectGrpcRPCs.kt:142)
	at com.google.firebase.dataconnect.core.DataConnectGrpcClient.executeMutation(DataConnectGrpcClient.kt:95)
	at com.google.firebase.dataconnect.core.MutationRefImpl.execute(MutationRefImpl.kt:65)
	at com.google.firebase.dataconnect.QuerySubscriptionIntegrationTest$collect_gets_notified_of_per_data_deserializer_successes$1.invokeSuspend(QuerySubscriptionIntegrationTest.kt:441)
	at com.google.firebase.dataconnect.QuerySubscriptionIntegrationTest$collect_gets_notified_of_per_data_deserializer_successes$1.invoke(Unknown Source:8)
	at com.google.firebase.dataconnect.QuerySubscriptionIntegrationTest$collect_gets_notified_of_per_data_deserializer_successes$1.invoke(Unknown Source:4)

The reason is that the DataConnectAuth object was used before its initialize() method was invoked. PR #6446 changed the logic to invoke initialize() in a separate coroutine, which introduced the race condition. By getting rid of the initialize() method altogether, and simply performing the initialization in an init block in DataConnectAuth (and DataConnectAppCheck) this race condition is completely eliminated.

Copy link
Contributor

github-actions bot commented Nov 8, 2024

📝 PRs merging into main branch

Our main branch should always be in a releasable state. If you are working on a larger change, or if you don't want this change to see the light of the day just yet, consider using a feature branch first, and only merge into the main branch when the code complete and ready to be released.

Copy link
Contributor

github-actions bot commented Nov 8, 2024

Vertex AI Mock Responses Check ⚠️

A newer major version of the mock responses for Vertex AI unit tests is available. update_responses.sh should be updated to clone the latest version of the responses: v5.2

Copy link
Contributor

github-actions bot commented Nov 8, 2024

Test Results

   54 files  ±0     54 suites  ±0   2m 15s ⏱️ +17s
  512 tests  - 3    511 ✅  - 3  1 💤 ±0  0 ❌ ±0 
1 024 runs   - 6  1 022 ✅  - 6  2 💤 ±0  0 ❌ ±0 

Results for commit 93c5daa. ± Comparison against base commit f20340a.

This pull request removes 5 and adds 2 tests. Note that renamed tests count towards both.
com.google.firebase.dataconnect.core.DataConnectAuthUnitTest ‑ close() should succeed if called _after_ initialize()
com.google.firebase.dataconnect.core.DataConnectAuthUnitTest ‑ close() should succeed if called _before_ initialize()
com.google.firebase.dataconnect.core.DataConnectAuthUnitTest ‑ forceRefresh() should throw if invoked before initialize()
com.google.firebase.dataconnect.core.DataConnectAuthUnitTest ‑ getToken() should return re-throw the exception thrown by InternalAuthProvider getAccessToken()
com.google.firebase.dataconnect.core.DataConnectAuthUnitTest ‑ getToken() should throw if invoked before initialize()
com.google.firebase.dataconnect.core.DataConnectAuthUnitTest ‑ close() should succeed if called on a brand new instance()
com.google.firebase.dataconnect.core.DataConnectAuthUnitTest ‑ getToken() should re-throw the exception thrown by InternalAuthProvider getAccessToken()

@dconeybe dconeybe changed the title dataconnect: DataConnectCredentialsTokenManager.kt: get rid of initialize() method and just initialize in the constructor dataconnect: DataConnectCredentialsTokenManager.kt: initialize synchronously to fix race condition Nov 9, 2024
@dconeybe dconeybe changed the title dataconnect: DataConnectCredentialsTokenManager.kt: initialize synchronously to fix race condition dataconnect: DataConnectCredentialsTokenManager: initialize synchronously to fix race condition Nov 9, 2024
@dconeybe dconeybe marked this pull request as ready for review November 9, 2024 03:17
@dconeybe dconeybe merged commit 4e2dcd0 into main Nov 12, 2024
43 checks passed
@dconeybe dconeybe deleted the dconeybe/dataconnect/CredentialsTokenManagerInitializeRaceConditionFix branch November 12, 2024 18:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants