简体   繁体   中英

How to prevent lag in Cloud Function when updating Firestore document

I want to make a cloud function execute faster and more reliably.

I am using Cloud Functions (CF) to receive a callback from a 3rd party server using https.onRequest. When this callback is received, the CF is triggered and the function executes with the goal of updating a particular Firestore document with information from the callback. The firestore document already exists and its ID is the same as a unique identifier from the callback.

To be clear, the code works and the document updates do happen; a lot of the time it's just extremely slow.

Sometimes the doc is updated instantly (great), but most of the time it takes a long time between the callback Cloud Function being executed, and the update actually happening. It has taken as long as 30 min in the past, which is not acceptable for the use case I'm building for. The problem is I am trying to notify the client as soon as the response from the 3rd party server is returned, as it indicates a successful payment.

I know the document is taking forever to be updated bc the client listens for real-time updates and I've also sat and watched the actual document in the firebase console not change for a long time, while other times (rarely) it's updated pretty quickly on both the client and in the console.

Why does the CF run immediately but the part that is responsible for updating the Firestore document lags for so long? I'm newish to Node.js.

This is the code I'm using for the CF:

app.post('/CallbackUrl', (req, res) => {

    let requestBody = req.body;
    let myPayload = JSON.stringify(requestBody);

    // Log successful function calls
    console.log('CF called');
    console.log(myPayload);

    const db = admin.firestore();


    if (requestBody.Body.stkCallback.ResultCode == 0) {


        // Transfer was a success
        console.log("transfer of funds was successful")
        let response = { "ResultCode": 0, "ResultDesc": "Success" }

        res.status(200).json(response);

        let crid = requestBody.Body.stkCallback.CheckoutRequestID; // also firestore doc id

        console.log(crid);

        const receiptNo = requestBody.Body.stkCallback.CallbackMetadata.Item[1].Value;
        console.log(receiptNo);

        // set the doc reference
        const txRef = db.collection('transactions').doc(crid);

        try {

// All console logs above this are registered immediately in the Firebase CF logs dashboard
// Below is the part that tends to take a very long time to execute, namely, the actual doc update

            txRef.get().then((doc) => {
                if (doc.exists) {
                    console.log('Document Found')
                    txRef.update({
                        transactionCode: `${receiptNo}`,
                        transactionType: "purchase",
                        transactionSuccess: true,
                    });
                } else {
                    console.log('No processing doc found');
                    return null;
                }
            });
        } catch (e) {
            console.log('Transaction failure:', e);
        }


    } else {
        res.status(400).json(response);
        console.log("transaction failed");
        let response = { "ResultCode": 1032, "ResultDesc": "Failed" }

    }
})

exports.txCallbackUrl = functions.https.onRequest(app);

Consider using async-await which simplifies the source code quite a bit. Remember, you must terminate the function on success and failure. If the function is consistently taking much longer than expected then, most likely, you aren't properly terminating the function. To terminate these cloud functions, return a promise or throw an HTTPS error.

const db = admin.firestore();

try {
    const txRef = db.collection("transactions").doc(crid);

    // Await the document from get()
    const doc = await txRef.get();

    // Document gotten

    if (doc.exists) {
        console.log("doc found");

        // Now await the write result from update()
        const writeResult = await txRef.update({
            transactionCode: "${receiptNo}",
            transactionType: "purchase",
            transactionSuccess: true,
        });

        // If we get this far then both async tasks succeeded.
        // If any failed with error, control is diverted to the catch block.
        console.log("doc updated");

        // Consider returning the write result to the client so it can confirm success.
        // You have to return something so return an empty promise if that makes more sense.
        return writeResult;
    } else {
        // The doc doesn't exist so consider what the client should see.
        // If it should get back an error, throw a JavaScript error here
        // and control will be diverted to the catch block below where you
        // can send an HTTPS error to the client.
        throw new Error("doc-missing");
    }
} catch (error) {
    if (error.message == "doc-missing") {
        // This was our doc-missing error, so now throw back an HTTPS error to the client.
        throw new functions.https.HttpsError("unknown", "doc-missing");
    }
    // This was not our custom error. It's either an error from the get or the update method.
    // Throwing these errors will automatically terminate our function.
    throw new functions.https.HttpsError("unknown", "some-custom-error-message", error);
}

If you use async-await , make sure to mark your function async which comes before the parameter list ( async (req, res) ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM