簡體   English   中英

如何 batch.commit() 用於 firestore 海量文檔?

[英]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 配額有關。 但我不知道哪個限制與此問題有關。

這很可能是因為您進行commitforEach沒有按預期工作。 一次又一次,帶有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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM