-
Notifications
You must be signed in to change notification settings - Fork 927
Defer check whether auth is available #4845
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
Conversation
|
8d2b847
to
07ccc28
Compare
Binary Size ReportAffected SDKs
Test Logs |
Size Analysis Report |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got an internal assertion failure unexpected state
if auth is never initialized, or initialized too late. And requests seem to be queued indefinitely until Auth is initialized.
The assertion should be gone as of 1e75d78 |
Thanks. The error went away. Now I found a use case where Firestore still doesn't wait for auth initialization:
For repro, you can clone https://github.com/Feiyang1/playground/tree/fs-auth-init and use the project in the You mentioned we may need to queue up work that uses the user, or even the auth, is that what we need to do to make it work? |
I think this will be solved by your "callback" PR that replaces the Promise in the initialization. Do you still have a link to that? I can apply this here or we can merge that PR before we test here further. |
Yes! https://github.com/firebase/firebase-js-sdk/commits/fei-firestore-wait You may need to resolve some merge conflicts. Thanks! |
I took the liberty to merge the component change into your PR, but It still doesn't work. |
I will take over from here :) |
Updated. Tested with no getDoc initially, with getDoc initially and with no getAuth. |
44f5c2f
to
6e9133e
Compare
This is now completely rewritten. The SDK now blocks on the availability of the use in the Credential Provider itself, which allows the SDK to block twice: Once when we check whether Auth is available at startup time and then once again if Auth is loaded later. We also synchronously invoke the user change handler (right as we release the block), which ensures that the user change gets executed before everything else. |
3e3015e
to
f0355ed
Compare
f0355ed
to
99b79f6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I understand most parts, but left a couple of questions.
I think we also need to do something similar in Database.
// raised. The initial event itself is invoked synchronously in | ||
// `awaitTokenAndRaiseInitialEvent()`. | ||
this.asyncQueue.enqueueRetryable(() => { | ||
this.changeListener(this.currentUser); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the registerAuth()
, we do
this.awaitTokenAndRaiseInitialEvent();
this.auth.addAuthTokenListener(this.tokenListener);
We already invoke this.changeListener
in the asyncQueue in awaitTokenAndRaiseInitialEvent
, should we not do it again in this.tokenListener
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I addressed this. I am not able to verify this right now, but will do a test run later today. BTW, this was meant to be addressed by the buggy code around initialEventRaised
* Whether the initial token event has been raised. This can go back to | ||
* `false` if Firestore first starts without Auth and Auth is loaded later. | ||
*/ | ||
private initialEventRaised: boolean = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This variable doesn't seem to do anything, because it's only set in awaitTokenAndRaiseInitialEvent
which is synchronous. It's not used in awaitTokenAndRaiseInitialEvent()
itself and is always set to true at the end of the function, so it seems the outside world will always see this variable as true
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, this bothers me. It is meant to prevent double-firing of the change listener. I tested that it doesn't fire twice manually, but what you say is also true - this variable and its check do not do anything. I have fixed the variable and am now resetting it on the queue rather than synchronously.
Co-authored-by: Feiyang <[email protected]>
Co-authored-by: Feiyang <[email protected]>
// Create a new deferred Promise that gets resolved when we receive the | ||
// next token. Ensure that all previous Promises also get resolved. | ||
const awaitToken = new Deferred<void>(); | ||
void awaitToken.promise.then(() => awaitToken.resolve()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then
only fires if awaitToken
resolves, why do we resolve it again in then
? The comment suggests it ensures all previous Promises also get resolved? But I don't get how that works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was meant to resolve the existing this.receivedUser Promise if another one was created later on. I changed how the listener gets called if auth is not used, and am no longer using the Promise twice. This allowed me to remove this logic.
* `awaitTokenAndRaiseInitialEvent()` is also used to block Firestore until | ||
* Auth is fully initialized. | ||
* | ||
* This function also invokes the change listener synchronously once a token |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean asynchronously, since it happens in the asyncQueue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant "synchronously with the code that resolves the token". I have reworded it.
} | ||
}, 0); | ||
|
||
this.awaitTokenAndRaiseInitialEvent(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this.asyncQueue
set at this point?
My understanding is it is not, so awaitTokenAndRaiseInitialEvent
doesn't do anything except for setting this.invokeChangeListener = false;
which will lead to wrong behavior if auth is not available - the following code in this.tokenListener` will not execute:
if (this.invokeChangeListener) {
this.asyncQueue!.enqueueRetryable(() =>
this.changeListener(this.currentUser)
);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to work in code though. Firestore will not receive a null user when auth is not initialized, but nothing seems to break.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the logic for invokeChangeListener
to only set it to true if the AsyncQueue is available. This should address this concern.
This should be ready for review again. |
if (auth) { | ||
registerAuth(auth); | ||
} else if (this.invokeChangeListener) { | ||
// If auth is still not available, invoke tokenListener once with null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment needs update.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
Tested the following manually, and they all worked. Yay!
|
Adds support for calling
getFirestore
before callinggetAuth
.Tested manually (our integration tests do not use auth).