#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
и будет удерживать пессимистическую блокировку всех возвращенных документов.