i have collection which contains the whole users products , and to avoid the casts of reading all docs to get the length of the collection , i increase one value 1 to every field once it's doc being created , ok now everything is ok
here i am using this method to chick if doc if exist or not so i can avoid the duplicate of counter in case offline mode
removeProduct1(){
FirebaseFirestore.instance.collection("product").doc('product1')
.get(const GetOptions(source: Source.server)).then((value1) {
if(value1.exists) {
FirebaseFirestore.instance.collection("product").doc('product1').delete();
FirebaseFirestore.instance.collection("product").doc('productCount').update({
'productCount': FieldValue.increment(-1),
});
}
}).onError((error, stackTrace) {
ScaffoldMessenger.of(context).showSnackBar(snackBar,);
});
}
also everything is ok , but i noticed if two user pressed the button for this method at the same time so its gonna be two value decrease -2 maybe someone will think its rarely when two user hit the button at the same time , well this is only simple sample of what i have for real instead , i have page which contains too many users which is not be rarely for being two user hit the button
EDIT FOR MOER EXPLATION
i have collection called Friends which contains User IDs of friends together
for example user1 his id is ABC and user2 his id is DEF
so id becomes final into the Friends collection like this ABCDEF by following function
and also this following function increase counter 1 for ever one of them
makeFriends(){
// here i make sure first if the friends Ids are not exists To avoid suspiciously increasing the counter in offline mode and its work very well
FirebaseFirestore.instance.collection("Friends").doc(user1Id user2Id).get().then((value) {
if(!value.exists){
// here to add ids to the friends collection
FirebaseFirestore.instance.collection("Friends").doc(user1Id user2Id).set({
'test':''
});
//here to increase counter in other colection which is users for both user
//user1
FirebaseFirestore.instance.collection("users").doc(user1Id).update({
'friendsCounter':FieldValue.increment(1)
});
//user2
FirebaseFirestore.instance.collection("users").doc(user2Id).update({
'friendsCounter':FieldValue.increment(1)
});
}
});
}
Okay now everything going fine and they became a friends with one value increased and their Ids became unit into Friends collection .. and here image for better explanation
here i have function responsible for deleting friend with decrease one value from FriendsCount Field for both of them
deleteFriends(){
// here i make sure first if the friends Ids are exists To
// avoid suspiciously decrease the counter in offline mode and its work very well
FirebaseFirestore.instance.collection("Friends").doc(user1Id user2Id).get().then((value) {
if(value.exists){
// here to remove ids from the friends collection
FirebaseFirestore.instance.collection("Friends").doc(user1Id user2Id).delete();
//here to decrease counter for both user
//user1
FirebaseFirestore.instance.collection("users").doc(user1Id).update({
'friendsCounter':FieldValue.increment(-1)
});
//user2
FirebaseFirestore.instance.collection("users").doc(user2Id).update({
'friendsCounter':FieldValue.increment(-1)
});
}
});
}
ok now also everything going fine .. lets say user1 deleted his friend user 2 , by the previous functionthe Friends id which is ABCDEFhas been deleted so next time if user 2 want to do the same opretion it will fail because Friends ids are not exist anymore ..
the problem IS :
lets say these two users are still friends and
these two user visited each others profiles and hit the button at the same time which is call the pervious function (deleteFriend) so they gonna get the same return value which is (yes exists) then will be double decrease value for both of them instead of one as required .
means if their friendsCount was 1 both of them when they was a friend so tee value will be -1 instead 0 because there two operation was proceed
and this happen only if these two user hit the button at the same moment , but if the user 1 hit the button before user 2 then user 2 want to do same operation so he ganna fail because friendId was deleted by user1 operation as a condition statement in previous function ..
if(value.exists) i thought it too fast to delete friendIds before it give same result to other user if they together call the function at the same time
of course i can prevent the button of calling the delete function in case one of these user is already deleted other but I didn't see this as a strong solution since i can't grantee traffic jam or any reason else like bad users behavior .. etc , also basically i can't do it because delete button must be available as long as they still a friends
Note: this also happen in addFriends buttom call too if two users hit it at the same time , double value 2 instead one ..
i need any way to avoid server to give same result if there was two user query same doc at the same time , or any good solution
CodePudding user response:
If you need to both read and write a document to update it, you should use a transaction to prevent the race condition that you now have when multiple users perform the operation around the same time.
Here you definitely want to use a transaction, because if the delete fails for whatever reason, the decrement operation should also fail (and vice versa).
The FlutterFire documentation on transactions also has a good explanation and example code to get started.
