Firestore operation is hanging - firebase

Looking for some suggestions to further debug this issue.
I'm writing a Flutter app and have successfully used Firestore for many different operations. However, bizarrely, a very simple operation refuses to complete. I never get a response from my await call:
#override
Future<void> reportSnap(FeedItem item) async {
print("Adding report to database: $item");
await Firestore.instance.collection(_REPORTS).add(
{
_ReportsKey.image: item.imageName,
_ReportsKey.reported: DateTime.now(),
_ReportsKey.reporter: _user.id,
},
);
print("-> Done"); // Never printed, nothing appears in DB
}
The code that calls this function also never completes:
if (wantsToReport) {
print("Sending report to data service");
await _dataService.reportSnap(item);
print("Showing snack message..."); // never shown
_view.showSnackMessage("Thanks, we'll take a look"); // doesn't happen
}
Other database operations continue to work correctly, which seems to rule out hitting any quotas on my free Firebase plan. (I think I'm a long way away from those quotas, just desktop testing at the moment).
I appreciate there isn't a lot to go on here, but this is stumping me. Has anyone hit any similar problems before?

Related

Firestore Native Client SDK cold start? (React Native Firebase)

In short: Is there some kind of cold start when connecting to Firestore directly from Client SDK
Hey. I'm using Firestore client sdk in Andoid and IOS application through #react-native-firebase.
Everything works perfectly but I have noticed weird behavior I haven't found explanation.
I have made logging to see how long it takes from user login to retrieve uid corresponding data from Firestore and this time has been ~0.4-0.6s. This is basically the whole onAuthStateChanged workflow.
let userLoggedIn: Date;
let userDataReceived: Date;
auth().onAuthStateChanged(async (user) => {
userLoggedIn = new Date();
const eventsRetrieved = async (data: UserInformation) => {
userDataReceived = new Date();
getDataDuration = `Get data duration: ${(
(userDataReceived.getTime() - userLoggedIn.getTime()) /
1000
).toString()}s`;
console.log(getDataDuration)
// function to check user role and to advance timing logs
onUserDataReceived(data);
};
const errorRetrieved = () => {
signOut();
authStateChanged(false);
};
let unSub: (() => void) | undefined;
if (user && user.uid) {
const userListener = () => {
return firestore()
.collection('Users')
.doc(user.uid)
.onSnapshot((querySnapshot) => {
if (querySnapshot && querySnapshot.exists) {
const data = querySnapshot.data() as UserInformation;
data.id = querySnapshot.id;
eventsRetrieved(data);
} else errorRetrieved();
});
};
unSub = userListener();
} else {
if (typeof unSub === 'function') unSub();
authStateChanged(false);
}
});
Now the problem. When I open the application ~30-50 minutes after last open the time to retrieve uid corresponding data from Firestore will be ~3-9s. What is this time and why does it happen? And after I open the application right after this time will be low again ~0.4-0-6s.
I have been experiencing this behavior for weeks. It is hard to debug as it happens only on build application (not in local environments) and only between +30min interval.
Points to notice
The listener query (which I'm using in this case, I have used also simple getDoc function) is really simple and focused on single document and all project configuration works well. Only in this time interval, which seems just like cold start, the long data retrieval duration occurs.
Firestore Rules should not be slowing the query as subsequent request are fast. Rules for 'Users' collection are as follows in pseudo code:
function checkCustomer(){
let data =
get(/databases/$(database)/documents/Users/$(request.auth.uid)).data;
return (resource.data.customerID == data.customerID);
}
match /Users/{id}{
allow read:if
checkUserRole() // Checks user is logged in and has certain customClaim
&& idComparison(request.auth.uid, id) // Checks user uid is same as document id
&& checkCustomer() // User can read user data only if data is under same customer
}
Device cache doesn't seem to affect the issue as application's cache can be cleaned and the "cold start" still occurs
Firestore can be called from another environment or just another mobile device and this "cold start" will occur to devices individually (meaning that it doesn't help if another device opened the application just before). Unlike if using Cloud Run with min instances, and if fired from any environment the next calls right after will be fast regardless the environment (web or mobile).
EDIT
I have tested this also by changing listener to simple getDoc call. Same behavior still happens on a build application. Replacing listener with:
await firestore()
.collection('Users')
.doc(user.uid)
.get()
.then(async document => {
if (document.exists) {
const data = document.data() as UserInformation;
if (data) data.id = document.id;
eventsRetrieved(data);
}
});
EDIT2
Testing further there has been now 3-15s "cold start" on first Firestore getDoc. Also in some cases the timing between app open has been only 10 minutes so the minimum 30 min benchmark does not apply anymore. I'm going to send dm to Firebase bug report team to see things further.
Since you're using React Native, I assume that the documents in the snapshot are being stored in the local cache by the Firestore SDK (as the local cache is enabled by default on native clients). And since you use an onSnapshot listener it will actually re-retrieve the results from the server if the same listener is still active after 30 minutes. From the documentation on :
If offline persistence is enabled and the listener is disconnected for more than 30 minutes (for example, if the user goes offline), you will be charged for reads as if you had issued a brand-new query.
The wording here is slightly different, but given the 30m mark you mention, I do expect that this is what you're affected by.
In the end I didn't find straight answer why this cold start appeared. I ended up changing native Client SDK to web Client SDK which works correctly first data fetch time being ~0.6s (always 0.5-1s). Package change fixed the issue for me while functions to fetch data are almost completely identical.

Firebase Messaging 7.0.0 to 9.0.1 conversion

I'm trying to set up FCM for my iOS flutter app and followed the guide by flutter and even the guide from Fireship.io
I was working with an older version of FCM before I updated the dependencies and now there are methods that are deprecated or just plainly left out. I am unsure of which methods to replace these and I was unable to find resources on what exactly changed in between versions.
Below is my code from FCM 7.0.0
Code:
final FirebaseMessaging _fcm = FirebaseMessaging.instance;
StreamSubscription iosSubscription;
#override
void initState() {
super.initState();
if (Platform.isIOS) {
iosSubscription = _fcm.onIosSettingsRegistered.listen((data) {
// save the token OR subscribe to a topic here
});
_fcm.requestNotificationPermissions(IosNotificationSettings);
}
}
_saveDeviceToken() async {
// Get the current user
String uid = FirebaseAuth.instance.currentUser.uid;
// FirebaseUser user = await _auth.currentUser();
// Get the token for this device
String fcmToken = await _fcm.getToken();
// Save it to Firestore
if (fcmToken != null) {
FirebaseFirestore.instance
.collection('users')
.doc(uid)
.collection('tokens')
.add({
'token': fcmToken,
'createdAt': FieldValue.serverTimestamp(), // optional
'platform': Platform.operatingSystem
});
}
}
So my problems lie with the onIosSettingsRegistered line and the requestNotificationPermissions.
For both it says that method is not defined, because it has been deprecated or is no longer a valid method. Obviously, before updating my dependencies it worked fine no errors. I'm looking for resources revolving around Firebase cloud Messaging 9.0.1 that might show what was updated and what are the current methods to use.
The above answer is what lead me to fixing my issues. I'm just outlining below what I pulled from the guide the other user posted
FirebaseMessaging() no longer works and instead use
FirebaseMessaging.instance
IosNotificationSettings which was used with requestNotificationPermissions() can now be done with just RequestPermission()
Setting up a FCM.configure no longer works and you should just use FirebaseMessaging.onMessage(), FirebaseMessaging.onMessageOpenedApp, and FirebaseMessaging.onBackGroundMessage(). The last one can be paired with BkacgroundMessageHandeler in order to set up notifications when the app is closed or suspended.
And lastly, saving tokens still works the same as 7.0.0 as it does in 9.1.0 so no changes there.
9.1.0 brings null safety to the table so (depending on the rest of your dependencies) you can run the newest version of flutter with null safety with no issues.
Simple Google searches yielded the following results, perhaps you can try a bit harder before posting a question next time:
onIosSettingsRegistered is deprecated
Usage of the IosNotificationSettings class is now deprecated (currently used with the now deprecated requestNotificationPermissions() method).
Instead of this class, use named arguments when calling requestPermission() and read the permissions back via NotificationSettings.
requestNotificationPermissions deprecated too
DEPRECATED: requestNotificationPermissions() has been deprecated in favor of requestPermission().

some codes don't work. i dont know why. how can i make the codes work?

I wanna make Login screen in unity. I'm gonna use firebase, and followed the manual in firebase page and some youtube channel to learn how to use firebase.
and.. some codes don't work. I used the codes that firebase give, and the codes that are below login success don't work. um.. sorry for my weak English. please see the codes. thanks.
this codes don't work
authUI.ShowLoggedInPanel();// 로그인 성공 시 메인메뉴로 전환!
authUI.LoggedInUserEmail.text = newUser.Email;
I don't know what i can try..
private void TryLoginWithFirebaseAuth(string email, string password) // 기존 사용자 로그인
{
auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("SignInWithEmailAndPasswordAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("SignInWithEmailAndPasswordAsync encountered an error: " + task.Exception);
return;
}
// 로그인 성공 (Maybe Login success?)
Firebase.Auth.FirebaseUser newUser = task.Result;
Debug.LogFormat("User signed in successfully: {0} ({1})",
newUser.DisplayName, newUser.UserId);
authUI.ShowLoggedInPanel();// 로그인 성공 시 메인메뉴로 전환!
authUI.LoggedInUserEmail.text = newUser.Email;
});
}
It doesn't show the error to me. but just.. it doesn't work.
Could someone help me out here.
I don't know Firebase well but the issue might be the threading.
All (the most) Unity API calls have to be done in the main thread. Since Firebase executes its stuff async in a background thread some calls might simply fail.
You should rather use an Action parameter in order to pass a callback to the method like e.g.
private void TryLoginWithFirebaseAuth(string email, string password, Action<Firebase.Auth.FirebaseUser> successCallback) // 기존 사용자 로그인
{
auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("SignInWithEmailAndPasswordAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("SignInWithEmailAndPasswordAsync encountered an error: " + task.Exception);
return;
}
// 로그인 성공 (Maybe Login success?)
Firebase.Auth.FirebaseUser newUser = task.Result;
successCallback?Invoke(newUser);
});
}
and use it like
TryLoginWithFirebaseAuth(someName, somePassword, onSuccess =>
{
Debug.LogFormat("User signed in successfully: {0} ({1})",
onSuccess.DisplayName, onSuccess.UserId);
authUI.ShowLoggedInPanel();// 로그인 성공 시 메인메뉴로 전환!
authUI.LoggedInUserEmail.text = onSuccess.Email;
}
this makes sure it is definitely executed in the Unity main thread.
It's hard to definitely answer your question without more context around it (ex: are there any useful logs in the Unity console?).
Some things to note:
You have to make sure you've setup Authentication in the Firebase Console. This means clicking Authentication in your side bar and explicitly enabling the methods you want:
Similarly, you need to make sure that your applications are properly setup. Your package id/bundle id (Android/iOS) need to match what's in your console. Similarly, you need to upload the SHA1 of your signing certificate to get it to work in Android.
I'm assuming that you're just debugging for now, so you can get your debug certificate here: https://developers.google.com/android/guides/client-auth
You'll want to open up project settings:
And add your fingerprint there:
Following up on #derHugo's suggest, threading may be an issue in addition to the basic setup instructions above. I wrote a post recently on how to work with threading and Firebase in Unity here: https://firebase.googleblog.com/2019/07/firebase-and-tasks-how-to-deal-with.html
The easiest way to make your code thread safe would be to replace ContinueWith with ContinueWithOnMainThread. This is an extension method on Task provided by the Firebase plugin to make it easier to work with.
I hope that all helps!
This is a classic Task continuation problem in Unity using Firebase. When you use, ContinueWith it is not ensured to be called on the main Unity thread. What you're trying to do authUI.ShowLoggedInPanel();authUI.LoggedInUserEmail.text = newUser.Email; requires to be executed on the Unity main thread. If you try to access GameObjects inside ContinueWith, that will fail. The code just ghosts out without any error in the console.
The solution:
Instead of ContinueWith, use ContinueWithOnMainThread from Firebase extensions which was made exactly for this reason.

Trouble returning a valid result while integrating Stripe API into my Meteor app

Here's the rundown:
I'm trying to run Stripe API on my Meteor app asynchronously
Long story short, everything works (i.e. subscription and charge is/are created normally and shows up in my Stripe dashboard)
When errors occur, the errors throw normally and show on client via user friendly alerts
I have a problem when there is a success and customer subscription is created, the result is not present in client and instead always returns as an error, despite it being a successful process
Here's what my method looks like on the server:
createCustomer: function(token, email, plan){
try{
let createCustomer = Meteor.wrapAsync(stripe.customers.create);
let result = createCustomer({
source: token,
email: email,
plan: plan
});
let subscription = {
customer: result.id,
sub: result.subscriptions.data[0].id,
plan: result.subscriptions.data[0].plan.name
};
Meteor.users.update({_id: Meteor.userId()}, {$set: subscription});
} catch(error){
if(error.code === "incorrect_cvc"){
throw new Meteor.Error("incorrect_cvc", error.message);
}
// More of such errors follows
}
}
Here's what it looks like on the client:
Stripe.card.createToken({
number: number,
cvc: cvc,
exp_month: exp,
exp_year: exp_year,
address_zip: zip,
address_country: country
}, function(status, response){
if(response.error){
console.log("Make sure all fields are filled before submitting order.");
} else{
let token = response.id;
Meteor.call("createCustomer", token, email, plan, function(error, result){
if(result){
console.log("Congratulations, everything worked!");
} else{
if(error.error === "incorrect_cvc"){
console.log("oops, the CSV is incorrect");
}
// More of such errors follow..
}
})
}
});
So, everything works in terms of when there is a real error, it throws fine on server + client. When user uses card, the charges are created and subscription is always created. HOWEVER, when there is a success and everything clicking fine, I still receive an error on client via callback and the result is never true or triggered. No idea why.
Not 100% up on Meteor, but it looks to me like your createCustomer method doesn't actually return anything, so the result from your (err, result) might never have anything in it?
As was mentioned in the comments, you might want to separate out the steps and wrap each in its own try-catch set so you can better isolate the issue.
Also, I feel like you could probably generalize your server-side error code to something like:
throw new Meteor.Error(error.error, error.message);
And I might even be tempted to do something like this, at least during testing/development - that way you can actually console.log() the original error in the browser:
throw new Meteor.Error(error.error, error.message, JSON.stringify(error));

Meteor Streams : client doesn't receive streams

i'm working on a simple app based on meteor and MeteorStreams.
The aim is simple :
one user will click on a button to create a room
other users will join the room
those users can emit streams with simple message
the creator will listen to that message and then display them
In fact : message from other users are sent (log in server script), but the creator doesn't receive them.
If i reload the page of the creator, then it will get messages sent from other user.
I don't really understand why it doesn't work the first time.
I use meteor-router for my routing system.
Code can be seen here
https://github.com/Rebolon/MeetingTimeCost/tree/feature/pokerVoteProtection
for the client side code is availabel in client/views/poker/* and client/helpers
for the server stream's code is in server/pokerStreams.js
Application can be tested here : http://meetingtimecost.meteor.com
The creator must be logged.
If you have any idea, any help is welcome.
Thanks
Ok, Ok,
after doing some debugging, i now understand what is wrong in my code :
it's easy in fact. The problem comes from the fact that i forgot to bind the Stream.on event to Deps.autorun.
The result is that this part of code was not managed by reactivity so it was never re-run automatically when the Session changed.
The solution is so easy with Meteor : just wrap this part of code inside the Deps.autorun
Meteor.startup(function () {
Deps.autorun(function funcReloadStreamListeningOnNewRoom () {
PokerStream.on(Session.get('currentRoom') + ':currentRoom:vote', function (vote) {
var voteFound = 0;
// update is now allowed
if (Session.get('pokerVoteStatus') === 'voting') {
voteFound = Vote.find({subscriptionId: this.subscriptionId});
if (!voteFound.count()) {
Vote.insert({value: vote, userId: this.userId, subscriptionId: this.subscriptionId});
} else {
Vote.update({_id: voteFound._id}, {$set: {value: vote}});
}
}
});
});
});
So it was not a Meteor Streams problem, but only my fault.
Hope it will help people to understand that outside Template and Collection, you need to wrap your code inside Deps if you want reactivity.

Resources