Android: как остановить транзакцию Firestore?

Сначала я запрашиваю некоторые документы в Firestore, используя этот queryWaitingRoom(), и вызываю runTransactionInWaitingRoom(), если результат удовлетворяет условию.

Вот первая функция:

private fun queryWaitingRoom() {
    waitingRoomRef
        .orderBy("isAvailable", Query.Direction.DESCENDING)
        .limit(1)
        .get()
        .addOnSuccessListener { documents ->

            if (!documents.isEmpty){
                for (document in documents) {
                    val waitingRoomId = document.get("id").toString()

                    runTransactionInWaitingRoom(waitingRoomId)
                }
            }
        }
}

Мой вопрос: в runTransactionInWaitingRoom(), если значение currentState не равно true, как я могу остановить transaction прямо в блоке else? и снова позвонить в queryWaitingRoom()? Вот вторая функция:

 private fun runTransactionInWaitingRoom(waitingRoomId: String){
    val docRef = waitingRoomRef.document(waitingRoomId)

    db.runTransaction { transaction ->

        val snapshot = transaction.get(docRef)
        val currentState = snapshot.getBoolean("isAvailable")

        if (currentState == true){
            transaction.update(docRef, "isAvailable", false)
        } else {
            ///// STOP THE TRANSACTION RIGHT HERE /////
            ///// And call the the queryWaitingRoom() again /////

            Log.d("OIWENCLKSJF", "Transaction.currentState != true")   // getting called 5 or 6 times.

            throw FirebaseFirestoreException("Population too high",    // only get called at the last attemp.
                FirebaseFirestoreException.Code.ABORTED)
        }

    }.addOnSuccessListener {

    }.addOnFailureListener { e ->
        Log.w("OIWENCLKSJF", "Transaction failure.", e)
    }
}

Что я получил до сих пор, так это то, что transaction продолжает вызывать себя, даже если я бросаю exception в блок else. У меня есть аналогичный вопрос прямо здесь, в SO, но это не помогло. Я буду очень признателен, если кто-то может немного направить меня.

Firestore, как остановить транзакцию?


person Junior    schedule 14.10.2020    source источник
comment
Пожалуйста, отредактируйте свой вопрос и добавьте структуру вашей базы данных в виде снимка экрана. Кроме того, является ли свойство isAvailable частью того же документа, что и id?   -  person Alex Mamo    schedule 14.10.2020
comment
Я не думаю, что структура базы данных имеет какое-либо отношение к этой проблеме. И да, свойство isAvailable взято из того же документа. Также isAvailable может иметь только два значения.. true и false. При первом создании он получает значение true.   -  person Junior    schedule 14.10.2020


Ответы (1)


И да, свойство isAvailable взято из того же документа.

Если оба свойства относятся к одному и тому же документу, транзакция выполняется только в том случае, если свойство isAvailable имеет значение true:

if (!documents.isEmpty){
    for (document in documents) {
        val waitingRoomId = document.get("id").toString()
        val isAvailable = document.getBoolean("isAvailable")
        if(isAvailable) {
            runTransactionInWaitingRoom(waitingRoomId)
        }
    }
}

Идея этого решения заключается в том, чтобы выполнять транзакцию только, когда isAvailable истинно. То, что вы делаете сейчас, вы всегда запускаете транзакцию, а внутри нее, если условие не выполняется, отменяете транзакцию. Почему это решение невозможно? Представьте, что у вас есть 1 миллион документов, и только у 1 из них есть isAvailable true. Готовы ли вы запустить 1 миллион транзакций ради одного документа? Боюсь, это не так. Помните, что в Firestore каждое выполненное чтение документа стоит одну операцию чтения.

Решение, которое еще лучше, состоит в том, чтобы напрямую запросить в базе данных документы, в которых свойство isAvailable содержит значение true:

waitingRoomRef
    .whereEqualTo("isAvailable", true) //Added
    .orderBy("isAvailable", Query.Direction.DESCENDING)
    .limit(1)
    .get()

В этом случае вы сразу получите нужные документы. Стоимость этой операции будет равна количеству документов, которые возвращает запрос.

person Alex Mamo    schedule 14.10.2020
comment
Я должен был объяснить это более подробно, но в моем случае это не сработает, даже если я использую .orderBy("isAvailable", true) . Потому что во время обновления isAvailable = false кто-то уже может изменить его значение на false. Тогда я снова столкнусь с той же проблемой - person Junior; 14.10.2020
comment
Это часть системы подбора игроков. Когда я обновляю поле isAvailable документа до false, это означает, что я связался с создателем документа. - person Junior; 14.10.2020
comment
Имеет смысл. В этом случае вам следует использовать прослушиватель в реальном времени, то есть что вы будете уведомлены об изменении, а затем сможете предпринять какие-либо действия. Вы всегда будете синхронизированы с серверами Firestore. - person Alex Mamo; 14.10.2020
comment
я использовал прослушиватели в реальном времени в других частях этой системы, но в этом случае мне нужно прочитать и обновить значение isAvailable до false, используя transaction, потому что многие пользователи могут получить один и тот же документ и попытаться изменить его значение на false в тот же момент . Поэтому я должен убедиться, что только один может изменить его. Что я делаю правильно в функции runTransaction..(), но я не нашел способа остановить transaction и снова запустить qurying, когда значение isAvailable уже изменено на false кем-то другим. - person Junior; 14.10.2020
comment
Если значение isAvailable можно изменить и из других частей вашего приложения, то правильно использовать транзакцию здесь и в другой части. У вас не может быть противоречивых данных. Поэтому нет необходимости повторно запускать запрос или останавливать транзакцию, просто используйте логику в моем ответе. - person Alex Mamo; 14.10.2020
comment
Мне нужно снова запустить запрос хотя бы на минуту, потому что я должен убедиться, что пользователь подключается к другому пользователю. В этом весь смысл этой системы сватовства. - person Junior; 14.10.2020
comment
Второе решение, предоставленное @AlexMamo, является лучшим способом сделать это, как уже упоминалось, вы извлекаете документы, которые не собираетесь использовать (за которые вам будет выставлен счет). Вы можете создать какую-то очередь, которая прослушивает изменения в реальном времени и при необходимости ставит в очередь пакет, не волнуйтесь, обновления Firestore настолько быстры, что они завершатся до того, как пользователь заметит это. С вашим решением о прерывании транзакций вы можете не завершить ни одной, если ваш пользователь постоянно переключается между комнатами. - person Emmanuel; 14.10.2020
comment
Привет, Джуниор, рад это слышать. Теперь, если вы считаете, что мой ответ вам помог, рассмотрите возможность его принятия, нажав на галочку (✔️) слева под стрелками голосования. Должен изменить цвет на зеленый. Я был бы очень признателен. Спасибо! - person Alex Mamo; 15.11.2020