I have a function that is decrementing user credits on firebase realtime database values with a transaction. As suggested in Firebase transaction API call current data is null transaction current value occasionally returns as null.
I've made a guard for the null case and returned 0 so the transaction function fires again until it gets the actual credit value.
function charge(cost, description) {
return new Promise((resolve, reject) => {
const creditRef = db.ref(`credits/${userid}`)
ref.transaction(function(current) {
console.log(`${description} current value: ${current}`)
if (current === null) {
console.log(`${description} returns 0 `)
return 0
}
if (cost > current || current === 0) {
//not enough credits return without committing
console.log(`${description} aborts `)
return
}
//commit the new credit value
console.log(`${description} returns ${current} - ${cost}`)
return current - cost
},
(error, commited, snapshot) => {
if (error) {
reject(error)
}
else {
if (commited) {
//user has enough credits
resolve()
}
else {
//not enough credits
reject('no commit')
}
}
})
}
However, in a case where 2 charge functions are fired back to back, the second call will get a current value of 0 (which is probably the returned 0 on the first charge call). So it will prematurely exit assuming that the user doesn't have enough credits. When both functions resolve the final credit value will be 3 and the second charge call will be ignored.
// User has 5 credits
charge(3, 'first call').then(() => console.log('first call success')
// transaction function returns 0 since current value is null
charge(2, 'second call').then(() => console.log('second call success')
Console log output:
first call current value: null
first call returns 0
second call current value: 0
second call aborts
first call current value: 5
first call returns 5 - 3
first call success
second call no commit
So the second charge call ends up not going through when user had enough credits. What is the proper way to handle firebase transaction null value cases?
In the case you get null from the database, you should make no change. Since you get null, also return null.
So:
const ref = firebase.database().ref(`59571450`)
function charge(cost, description) {
return new Promise((resolve, reject) => {
ref.transaction(function(current) {
console.log(`${description} current value: ${current}`);
if (current === null) {
console.log(`${description} returns null`)
return null;
}
if (cost > current) {
//not enough credits return without committing
console.log(`${description} aborts `)
return
}
//commit the new credit value
console.log(`${description} returns ${current} - ${cost}`)
return current - cost
},
(error, commited, snapshot) => {
if (error) {
reject(error)
}
else {
if (commited) {
//user has enough credits
resolve()
}
else {
//not enough credits
reject('no commit')
}
}
});
});
}
ref.set(5).then(() => {
charge(3, 'first call').then(() => console.log('first call success'))
charge(2, 'second call').then(() => console.log('second call success'))
})
When run this gives this output:
first call current value: null
first call returns null
second call current value: null
second call returns null
first call current value: 5
first call returns 5 - 3
second call current value: 2
second call returns 2 - 2
first call success
second call success
For a working version of this code, see: https://jsbin.com/xefebul/1/edit?js,console
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With