Как запросить вложенную коллекцию внутри транзакции в firebase?

#javascript #database #firebase #google-cloud-firestore

#javascript #База данных #firebase #google-облако-firestore

Вопрос:

У меня есть вложенная коллекция с именем requests внутри моей корневой коллекции tradeRequests

Я хочу запросить свою requests вложенную коллекцию, используя транзакции в firebase

Приведенный ниже код завершится ошибкой, потому что Transaction.get() requires its first argument to be a DocumentReference

 const getOne = (itemID) => db.runTransaction(async (transaction) => {
  const tradeRequestItemRef = tradeRequestsRef.doc(itemID);
  const requestsRef = tradeRequestItemRef.collection('requests');

  const rawTradeRequestItem = await transaction.get(tradeRequestItemRef);

  if (!rawTradeRequestItem) return null;

  // something like this ???
  // But this line will break everything
  // Error: Transaction.get() requires its first argument to be a DocumentReference
  const rawRequests = transaction.get(requestsRef);

  return {
    ...normalizeData(rawTradeRequestItem),
    requests: normalizeData(rawRequests),
  };
});
  

Ответ №1:

В SDK для веб-и мобильных клиентов вы не можете выполнять транзакции по результатам запроса в рамках той же транзакции. Вы можете получить () только каждый документ по отдельности, используя DocumentReference . Итак, что вы можете сделать вместо этого, это сначала выполнить запрос, а затем get() ссылку на каждый отдельный документ из этого запроса в транзакции, чтобы убедиться, что он обрабатывается атомарно.

 // query for requests before the transaction
const requests = await db.collection("requests").get()

await db.runTransaction(async transaction => {
    // get each document individually again in the transaction
    const requestsInTransaction = []
    for (const doc of requests.docs) {
        const requestInTransaction = await transction.get(doc.ref)
        requestsInTransaction.push(requestInTransaction)
    }

    // Work with each document as needed...
    return whatever_you_want
})
  

Обратите внимание, что если результаты исходного запроса изменятся во время транзакции, транзакция не увидит никаких новых или удаленных документов.

Комментарии:

1. В последнем предложении вы сказали, что изменения исходного запроса при выполнении транзакции не будут видны транзакции. Затем…. какой смысл вообще использовать транзакции в коллекциях ?!

2. @dontdownvoteme Для клиентских приложений транзакции требуют, чтобы вы знали каждый документ, с которым хотите выполнить транзакцию, во время транзакции. На самом деле это очень распространенная ситуация. Обработка всей коллекции или результатов запроса (которые могут быть огромными) приведет к снижению производительности.

3. как насчет серверной части? Если вы запустите транзакцию запроса, будет ли это гарантировать, что коллекция не изменится, пока транзакция не будет завершена?

4. @dontdownvoteme Нет, вы можете гарантировать, что не изменится до 500 отдельных документов. Если вы получаете их из запроса к коллекции, отлично. Но это все равно будет максимум 500, чтобы предотвратить проблемы с производительностью и масштабируемостью.

Ответ №2:

Как объясняется в документе «Сериализуемость и изоляция транзакций» для Firestore, мобильные / веб-SDK (iOS, Android, Web, C ) используют оптимистичные элементы управления параллелизмом.

В документе объясняется, что:

Транзакция отслеживает все документы, которые вы читаете внутри транзакции … и завершает свои операции записи, только если ни один из этих документов не изменился во время выполнения транзакции. Если какой-либо документ изменился, обработчик транзакции повторяет транзакцию. Если транзакция не может получить чистый результат после нескольких попыток, транзакция завершается неудачно из-за конфликта данных.

Таким образом, риск возникновения слишком большого количества сбоев при сопоставлении данных при отслеживании всех документов, возвращаемых запросом, считается слишком высоким, поэтому мобильные / веб-SDK не дают возможности выполнить запрос в транзакции.


С другой стороны, пакеты SDK администратора используют пессимистические элементы управления параллелизмом, поэтому вы можете выполнить запрос в транзакции и установить пессимистическую блокировку на все документы, возвращаемые запросом. Итак, одним из решений является использование вызываемой облачной функции (которая использует Admin SDK) и вызов этой облачной функции из вашего клиента.

Более конкретно, вы бы использовали в своей облачной функции get() метод из Node.js Серверный SDK для Google Cloud Firestore, который может быть передан Query и будет удерживать пессимистическую блокировку всех возвращенных документов.