1. ホーム
  2. node.js

[解決済み] Firebaseのクラウド機能が非常に遅い

2022-04-23 19:01:01

質問

新しいfirebase cloudの機能を利用したアプリケーションを開発しています。現在起こっていることは、トランザクションがキューノードに入れられるということです。そして、関数がそのノードを削除し、正しいノードに置くというものです。これは、オフラインで作業することができるため、実装されています。

現在の問題は、この関数のスピードです。関数自体は400msくらいで終わるので、まあまあです。しかし、エントリーがすでにキューに追加されているのに、関数が非常に長い時間(約8秒)かかることがあるのです。

サーバーの起動に時間がかかっていると思われるのは、最初のアクションの後にもう一回アクションを行ったときです。というのも、最初のアクションの後にもう一度アクションを実行すると、時間が大幅に短縮されるからです。

この問題を解決する方法はあるのでしょうか?この下に、私たちの関数のコードを追加しました。何も問題はないと思われますが、念のため追加しておきました。

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const database = admin.database();

exports.insertTransaction = functions.database
    .ref('/userPlacePromotionTransactionsQueue/{userKey}/{placeKey}/{promotionKey}/{transactionKey}')
    .onWrite(event => {
        if (event.data.val() == null) return null;

        // get keys
        const userKey = event.params.userKey;
        const placeKey = event.params.placeKey;
        const promotionKey = event.params.promotionKey;
        const transactionKey = event.params.transactionKey;

        // init update object
        const data = {};

        // get the transaction
        const transaction = event.data.val();

        // transfer transaction
        saveTransaction(data, transaction, userKey, placeKey, promotionKey, transactionKey);
        // remove from queue
        data[`/userPlacePromotionTransactionsQueue/${userKey}/${placeKey}/${promotionKey}/${transactionKey}`] = null;

        // fetch promotion
        database.ref(`promotions/${promotionKey}`).once('value', (snapshot) => {
            // Check if the promotion exists.
            if (!snapshot.exists()) {
                return null;
            }

            const promotion = snapshot.val();

            // fetch the current stamp count
            database.ref(`userPromotionStampCount/${userKey}/${promotionKey}`).once('value', (snapshot) => {
                let currentStampCount = 0;
                if (snapshot.exists()) currentStampCount = parseInt(snapshot.val());

                data[`userPromotionStampCount/${userKey}/${promotionKey}`] = currentStampCount + transaction.amount;

                // determines if there are new full cards
                const currentFullcards = Math.floor(currentStampCount > 0 ? currentStampCount / promotion.stamps : 0);
                const newStamps = currentStampCount + transaction.amount;
                const newFullcards = Math.floor(newStamps / promotion.stamps);

                if (newFullcards > currentFullcards) {
                    for (let i = 0; i < (newFullcards - currentFullcards); i++) {
                        const cardTransaction = {
                            action: "pending",
                            promotion_id: promotionKey,
                            user_id: userKey,
                            amount: 0,
                            type: "stamp",
                            date: transaction.date,
                            is_reversed: false
                        };

                        saveTransaction(data, cardTransaction, userKey, placeKey, promotionKey);

                        const completedPromotion = {
                            promotion_id: promotionKey,
                            user_id: userKey,
                            has_used: false,
                            date: admin.database.ServerValue.TIMESTAMP
                        };

                        const promotionPushKey = database
                            .ref()
                            .child(`userPlaceCompletedPromotions/${userKey}/${placeKey}`)
                            .push()
                            .key;

                        data[`userPlaceCompletedPromotions/${userKey}/${placeKey}/${promotionPushKey}`] = completedPromotion;
                        data[`userCompletedPromotions/${userKey}/${promotionPushKey}`] = completedPromotion;
                    }
                }

                return database.ref().update(data);
            }, (error) => {
                // Log to the console if an error happened.
                console.log('The read failed: ' + error.code);
                return null;
            });

        }, (error) => {
            // Log to the console if an error happened.
            console.log('The read failed: ' + error.code);
            return null;
        });
    });

function saveTransaction(data, transaction, userKey, placeKey, promotionKey, transactionKey) {
    if (!transactionKey) {
        transactionKey = database.ref('transactions').push().key;
    }

    data[`transactions/${transactionKey}`] = transaction;
    data[`placeTransactions/${placeKey}/${transactionKey}`] = transaction;
    data[`userPlacePromotionTransactions/${userKey}/${placeKey}/${promotionKey}/${transactionKey}`] = transaction;
}

解決方法は?

ファイヤーベイザーはこちら

いわゆる機能のコールドスタートが起きているようですね。

しばらく関数が実行されていない場合、Cloud Functionsは関数をより少ないリソースで実行できるモードにします。そして、再び関数を叩くと、このモードから環境が復元される。復元にかかる時間は、固定コスト(例:コンテナの復元)と一部変動コスト(例:ノードモジュールを多く使っている場合は時間がかかる)で構成されている。

私たちはこれらの操作のパフォーマンスを継続的に監視し、開発者の体験とリソースの使用との間の最適な組み合わせを確保しています。そのため、これらの時間は時間の経過とともに改善されることが期待されます。

良いニュースは、これは開発中にしか経験しないはずだということです。一度、実稼働環境で関数が頻繁に起動されるようになれば、コールドスタートに陥ることはほとんどありません。