Reputation: 12735
I need to call multiple functions using the same transaction Object, The use case is when I need to make sure that all functions execute effectively or None for instance when money is transferred I need to make sure the user balance is decreased, the other user balance is increased and history of this money exchange is save. (It's not a real money app, don't ask why I am doing this in client side).
My question is this function execute(list of functions)
sometimes gives appropriate results sometimes it skips some functions OR I am not sure, but the data in Firestore Database says it all. They are sometimes partially written and sometimes full written.
fun execute(functions: List<(Transaction) -> Unit>) {
if (_loadingState.value == LoadingState.Loading) return //Avoid double tapping
_loadingState.value = LoadingState.Loading
viewModelScope.launch {
try {
Firebase.firestore.runTransaction { transaction ->
for (function in functions) {
function(transaction)
}
}.addOnSuccessListener {
_loadingState.value = LoadingState.Success
}.addOnFailureListener {
_loadingState.value =
LoadingState.Error("Failed, Error occurred: ${it.message}")
}
} catch (e: Exception) {
_loadingState.value = LoadingState.Error("Failed, Error occurred: ${e.message}")
}
}
}
Should I remove the viewModelScope.launch
should I put a return value in the runTransaction
block. I need the actual solution as I said the problem is not predictable so to test/debug I need to experiment on real data and after even 10 to 30 calls the transaction partial writes can appear.
fun assignSender(user: User, order: Order) {
if (user.userID.isBlank() || user.name.isBlank() || user.userID == order.sendBy) return
_actionStarted = true
val functions = listOf<(Transaction) -> Unit> { transaction ->
assignSenderTransaction(transaction, user, order)
HistoryViewModel.saveHistory(transaction, HistoryViewModel.toHistoryAssign(order, user))
}
execute(functions)
}
This is one example where I call execute
I basically use the function execute
with function that takes at least one transaction argument. The other functions are:
private fun assignSenderTransaction(transaction: Transaction, user: User, order: Order) {
val orderRef = firestore
.collection(Collections.ORDERS).document(order.id)
val updates =
mapOf("sendBy" to user.userID, "senderName" to user.name, "senderPhone" to user.phone)
transaction.update(orderRef, updates)
}
fun saveHistory(transaction: Transaction, history: History) {
val historyRef = Firebase.firestore
.collection(Collections.HISTORYS).document(history.id)
transaction.set(historyRef, history)
}
All I do is make sure Reading Functions starts before Writing Ones. In case of this function sometimes history is not written.
UPDATE:
There is this function here after I have reviewed all the code, I find does not follow Firestore limits of 1 write per second rule AND also some of the map-field-fields may have special characters like products={"key+-":5}
which might not be the way dot syntax works
fun incrementStock(
transaction: Transaction,
userID: String,
userName: String,
products: HashMap<String, Int>,
increase: Boolean
) {
val stockRef = Firebase.firestore.collection(Collections.STOCKS).document(userID)
for ((product, quantity) in products) {
transaction.update(
stockRef,
"products.$product",
FieldValue.increment(if (increase) quantity.toLong() else -quantity.toLong())
)
}
HistoryViewModel.saveHistory(
transaction,
HistoryViewModel.toHistory(products, userID, userName, increase)
)
}
Can the transaction succeed even if some of the writes in the loop fail as long as the the document was updated at least once? If the key in the products
map has characters like _-=+ will the map.fieldOfMap fail to write? If yes does it guarantee the transaction will also fail?
Upvotes: 0
Views: 37