-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
fix(firestore, ios): transaction atomicity failure fix #3599
Conversation
…tase/react-native-firebase into @russell/firestore-transaction-ios
Codecov Report
@@ Coverage Diff @@
## master #3599 +/- ##
==========================================
+ Coverage 54.39% 55.91% +1.52%
==========================================
Files 112 108 -4
Lines 3668 3436 -232
Branches 194 0 -194
==========================================
- Hits 1995 1921 -74
+ Misses 1602 1515 -87
+ Partials 71 0 -71 |
Needs a JS test to confirm transaction rollback. |
…tase/react-native-firebase into @russell/firestore-transaction-ios
Please note: Rollback appears to be successful when you need to rollback on a single property (incrementing a number property for example). I was only able to reproduce the bug when it was two separate properties updated in a db record. |
PR title please 🙃 |
I'm not sure the test is sufficiently confirming the previous behaviour in #2805 has been fixed, I rolled back your changes and this test still passes; which indicates to me that it's not fixing previous behaviour? If it helps I made a test out of the users code that passes, not checked if it fails previously: it.only('should roll back any updates that failed', async () => {
await firebase
.firestore()
.collection('v6/transactions/matches')
.doc('123')
.set({
turn: 0,
score_user_1: 0,
score_user_2: 0,
});
const createMove = async (uid, matchId, turn, delay = 0) => {
// console.warn(`createMove ${uid} turn: ${turn}`);
const matchRef = firebase
.firestore()
.collection('v6/transactions/matches')
.doc(matchId);
return firebase.firestore().runTransaction(async transaction => {
// console.warn(`BEGIN transaction ${uid} turn: ${turn}`);
// READ
// console.warn(`READ BEGIN ${uid}`);
const match = await transaction.get(matchRef);
const matchData = match.data();
// console.warn(`READ DONE ${uid} - turn: ${matchData.turn}`);
// CHECK & PROCESS
if (matchData.turn !== turn) {
const message = `Sorry ${uid}! Another player was faster!`;
// console.warn(`ABORT transaction ${uid} turn: ${turn}`);
throw new Error(message);
}
const newTurn = matchData.turn || 1;
const newScore = matchData[`score_${uid}`] || 1;
// WRITE
const updateObject = {
turn: newTurn,
[`score_${uid}`]: newScore,
};
// console.warn(
// `WRITE ${uid} - turn: ${updateObject.turn} score_${uid}: ${updateObject[`score_${uid}`]}`,
// );
await Utils.sleep(delay);
// THIS UPDATE SHOULD BE ROLLED BACK
// when the updateFunction is aborted second attempt
transaction.update(matchRef, updateObject);
// console.warn(`END transaction ${uid}`);
});
};
const promises = [createMove('user_1', '123', 0, 500), createMove('user_2', '123', 0, 200)];
try {
await Promise.all(promises);
} catch (e) {
e.message.should.equal(`Sorry user_1! Another player was faster!`);
}
const result = await firebase
.firestore()
.collection('v6/transactions/matches')
.doc('123')
.get();
result.data().should.eql(jet.contextify({ turn: 1, score_user_2: 1, score_user_1: 0 }));
}); |
Going to go ahead an merge this, though still could do with the test updating, will track on |
fixes #2805
Notes
aborted
variable was being used asabort
(maybe use constants for property names?)__block
from variable on line 122 as it isn't used in a block.complete
variable as thecompleteBlock
shouldn't run before the transaction has occurred so it'll never be set totrue
. ThecompleteBlock
should also run just once in that context so settingcomplete
totrue
shouldn't matter. I could be wrong so happy to change if that's the case.