[英]How to batch.commit() for firestore massive documents?
我有大約 31,000 個文檔要batch.commit()
。
我正在使用 Blaze 計划。
一個批次最多可以攜帶 500 個文檔。 因此,我將 490 個文檔分成批次。 我有 65 批。
這是我的 firebase function 代碼:
'use strict';
const express = require('express');
const cors = require('cors');
const axios = require('axios');
// Firebase init
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const firestore = admin.firestore();
const echo = express().use(cors()).post("/", (request, response) => {
axios.get('https://example.com/api').then(result => {
const data = result.data.items;
let batchArray = [];
let batchIndex = 0;
let operationCounter = 0;
//initiate batch;
batchArray.push(firestore.batch());
data.forEach(item => {
const collectionRef = firestore.collection('items').doc();
const row = {
itemName: item.name,
// ... and so on...
};
batchArray[batchIndex].set(collectionRef, row);
operationCounter++;
if (operationCounter === 490) {
batchArray.push(firestore.batch());
functions.logger.info(`Batch index added.`, batchIndex);
batchIndex++;
operationCounter = 0;
}
});
/*
This code wrote only 140 documents.
Throws Error: 4 DEADLINE_EXCEEDED: Deadline exceeded
batchArray.forEach(batch => {
batch.commit()
.then(result=> functions.logger.info("batch.commit() succeeded:", result) )
.catch(error=>functions.logger.info("batch.commit() failed:", error));
})
*/
/*
This code wrote only 630 documents
Throws Error: 4 DEADLINE_EXCEEDED: Deadline exceeded
Promise.all([
batchArray.forEach(batch => {
setTimeout(
()=>batch.commit().then(result=> functions.logger.info("batch.commit() succeeded:", result) ).catch(error=>functions.logger.info("batch.commit() failed:", error)),
1000);
})
]).catch(error => functions.logger.error("batch.commit() error:", error));
*/
// This code wrote 2100 documents.
return Promise.all([
batchArray.forEach(batch => {
batch.commit()
.then(result => functions.logger.info("batch.commit() succeeded:", result))
.catch(error => functions.logger.warn("batch.commit() failed:", error))
})
]).then(result => {
functions.logger.info("all batches succeeded:", result);
return response.status(200).json({ "status": "success", "data": `success` });
})
.catch(error => {
functions.logger.warn("all batches failed:", error);
return response.status(200).json({ "status": "error", "data": `${error}` });
});
}).catch(error => {
functions.logger.error("HTTPS Response Error", error);
return response.status(204).json({ "status": "error", "data": `${error}` });
});
});
exports.echo = functions.runWith({
timeoutSeconds: 60 * 9,
}).https.onRequest(echo);
幾秒鍾后,我得到了“成功”的回應。 但是插入的 firestore 數據僅在 7 分鍾后出現,並且在雲功能日志中,它顯示錯誤日志,65 批中有 5 批成功。
拋出的錯誤是:
batch.commit() failed: { Error: 4 DEADLINE_EXCEEDED: Deadline exceeded
at Object.callErrorFromStatus (/workspace/node_modules/@grpc/grpc-js/build/src/call.js:31:26)
at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client.js:176:52)
at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:342:141)
at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:305:181)
at process.nextTick (/workspace/node_modules/@grpc/grpc-js/build/src/call-stream.js:124:78)
at process._tickCallback (internal/process/next_tick.js:61:11)
Caused by: Error at WriteBatch.commit (/workspace/node_modules/@google-cloud/firestore/build/src/write-batch.js:419:23)
at Promise.all.batchArray.forEach.batch (/workspace/index.js:100:23)
at Array.forEach (<anonymous>) at axios.get.then.result (/workspace/index.js:99:24)
at process._tickCallback (internal/process/next_tick.js:68:7) code: 4, details: 'Deadline exceeded', metadata: Metadata { internalRepr: Map {}, options: {} }, note: 'Exception occurred in retry method that was not classified as transient' }
錯誤Error: 4 DEADLINE_EXCEEDED
可能與 firestore 配額有關。 但我不知道哪個限制與此問題有關。
這很可能是因為您進行commit
的forEach
沒有按預期工作。 一次又一次,帶有forEach
function 的await
會導致這樣的問題。 很久以前,我認為由於await
在 forEach 中,它會等到它完成,然后 go 到數組中的下一個項目,但事實並非如此。 它將同時運行它們。 我建議使用傳統的 for 循環。
另外,我建議不要使用 then 語法。 在這個原因下,它仍然會同時運行它們。 嘗試將 await 與傳統的 for 循環結合使用。 這將解決您的問題。
另一件事,你的 Promise.all 在這里沒有幫助。 Promise.all 用於同時運行多個命令,但由於超出錯誤,您需要一次運行它們(我知道,因為有這么多命令,所以很糟糕)。
for (const batch of batchArray) {
await batch.commit()
}
我不確定在獲得超出的數量之前需要多少次提交(使用上述方法),但我很好奇您是否一次進行 2-3 次提交或其他什么。 但是,通常最好一次做一個。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.