157 votes

Les fonctions cloud de Firebase sont très lentes

Nous travaillons sur une application qui utilise la nouvelle firebase cloud fonctions. Ce qui se passe est qu'une transaction est mis dans la file d'attente de nœud. Et puis, la fonction supprime le nœud et la met dans le bon noeud. Cela a été mis en œuvre en raison de la capacité à travailler en mode hors connexion.

Notre problème actuel est la vitesse de la fonction. La fonction elle-même prend environ 400ms, donc c'est bien. Mais parfois, les fonctions prennent un temps très long (environ 8 secondes), tandis que l'entrée a déjà été ajouté à la file d'attente.

Nous soupçonnons que le serveur met du temps à démarrer, parce que quand nous faisons de l'action une fois de plus après la première. Il prend beaucoup moins de temps.

Est-il possible de résoudre ce problème? Ici j'ai ajouté le code de notre fonction. Nous soupçonnons qu'il n'y a rien de mal à cela, mais nous avons ajouté juste au cas où.

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;
}

133voto

Frank van Puffelen Points 16029

firebaser ici

Il semble que vous êtes en train de vivre une soi-disant de démarrage à froid de la fonction.

Lorsque votre fonction n'a pas été exécuté dans un certain temps, le Cloud Fonctions le met dans un mode qui utilise moins de ressources. Puis, quand vous frappez la fonction, il restaure l'environnement à partir de ce mode. Le temps nécessaire à la restauration se compose d'un coût fixe (par exemple, restaurer le contenant) et d'une partie variable de coût (par exemple, si vous utilisez beaucoup de nœud de modules, il peut prendre plus de temps).

Nous sommes continuellement à la surveillance de la performance de ces opérations afin d'assurer la meilleure combinaison entre l'expérience de développement et d'utilisation des ressources. Alors attendez-vous ces temps de s'améliorer au fil du temps.

La bonne nouvelle est que vous ne devriez rencontrer ce cours de développement. Une fois que vos fonctions sont souvent déclenché dans la production, les chances sont qu'ils vont presque jamais frapper un démarrage à froid à nouveau.

65voto

Tyris Points 25

Mise à jour - ressemble à beaucoup de ces problèmes peuvent être résolus à l'aide de la variable cachée process.env.FUNCTION_NAME comme on le voit ici: https://github.com/firebase/functions-samples/issues/170#issuecomment-323375462

Mise à jour avec le code - Par exemple, si vous avez le fichier d'index:

...
exports.doSomeThing = require('./doSomeThing');
exports.doSomeThingElse = require('./doSomeThingElse');
exports.doOtherStuff = require('./doOtherStuff');
// and more.......

Ensuite, tous vos fichiers seront chargés, et tous ces fichiers sera également chargé, résultant dans un grand nombre de généraux et de polluer votre portée globale pour l'ensemble de vos fonctions.

Au lieu de séparer votre comprend:

if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === 'doSomeThing') {
  exports.doSomeThing = require('./doSomeThing');
}
if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === 'doSomeThingElse') {
  exports. doSomeThingElse = require('./doSomeThingElse');
}
if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === 'doOtherStuff') {
  exports. doOtherStuff = require('./doOtherStuff');
}

Ce sera seulement de charger le fichier requis(s) lorsque cette fonction est spécialement appelé; vous permettant de garder votre portée globale beaucoup plus propre qui devrait accélérer froid-bottes.


Cela devrait permettre une beaucoup plus propre solution que de ce que j'ai fait ci-dessous (même si l'explication ci-dessous tient toujours).


Réponse Originale À Cette Question

Il ressemble à qui nécessitent des fichiers et général de l'initialisation se passe dans le domaine global est une grande cause de ralentissement pendant les froides-boot.

Comme un projet devient de plus en plus des fonctions de la portée globale d'une pollution de plus en plus et aggrave le problème, surtout si vous l'étendue des fonctions dans des fichiers séparés (comme par exemple en utilisant Object.assign(exports, require('./more-functions.js')); votre index.js.

J'ai réussi à voir des gains énormes dans le froid de démarrage de la performance par le déplacement de toutes mes nécessite une méthode init comme ci-dessous puis en l'appelant comme la première ligne à l'intérieur de toute définition de fonction pour ce fichier. Par exemple:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
// Late initialisers for performance
let initialised = false;
let handlebars;
let fs;
let path;
let encrypt;

function init() {
  if (initialised) { return; }

  handlebars = require('handlebars');
  fs = require('fs');
  path = require('path');
  ({ encrypt } = require('../common'));
  // Maybe do some handlebars compilation here too

  initialised = true;
}

J'ai vu des améliorations à partir d'environ 7-8 à 2-3 lors de l'application de cette technique à un projet avec ~30 fonctions de l'ensemble des 8 fichiers. Cela semble également provoquer des fonctions à besoin d'être redémarré à froid de moins en moins souvent (sans doute dû à une plus faible utilisation de la mémoire?)

Malheureusement, cela fait toujours HTTP fonctions à peine utilisable pour l'utilisateur face à une utilisation en production.

En espérant que le Firebase équipe ont certains plans futurs pour permettre la délimitation des fonctions de sorte que seules les modules doivent être chargés pour chaque fonction.

9voto

Mr.hands-on Points 107

Je suis face à des problèmes semblables avec firestore cloud fonctions. Le plus important est la performance. Spécialement dans le cas de la phase précoce de startups, lorsque vous ne pouvez pas payer vos premiers clients de voir "lente" des applications. Une simple génération de documentation de la fonction pour l'e.g donne ça:

-- Exécution de la fonction a pris 9522 ms, fini avec le code d'état: 200

Alors: j'ai eu un compliqué page termes et conditions. Avec le cloud fonctions de l'exécution en raison de la démarrage à froid serait d'environ 10 à 15 secondes, même de temps en temps. J'ai ensuite déménagé à un node.js app, hébergé sur appengine conteneur. Le temps est venu jusqu'à 2 à 3 secondes.

J'ai été comparant à de nombreuses fonctions de mongodb avec firestore et parfois moi aussi je me demande si au cours de cette première phase de mon produit je dois aussi passer à une autre base de données. Le plus grand adv que j'avais en firestore a été l'élément déclencheur de la fonctionnalité onCreate, onUpdate d'objets document.

https://db-engines.com/en/system/Google+Cloud+Firestore%3BMongoDB

En gros si il y a des parties statiques de votre site qui peuvent être déchargées à appengine de l'environnement, peut-être pas une mauvaise idée.

3voto

J'ai fait toutes ces choses, ce qui améliore les performances une fois que les fonctions sont réchauffé, mais le démarrage à froid est en train de me tuer. L'un des autres problèmes que j'ai rencontré avec de la scro, car il faut être deux voyages vers le cloud fonctions pour faire le travail. Je suis sûr que je peux résoudre ce problème, cependant.

Lorsque vous avez une application dans son début (demo) de la phase quand il n'est pas utilisé fréquemment, les performances ne sont pas va être génial. C'est quelque chose qui devrait être considéré, comme des adopteurs précoces, avec un début de produit besoin de regarder leur meilleur en face de clients potentiels/investisseurs. Nous avons adoré la technologie, donc nous avons migré à partir d'anciennes essayé-et-vrai des cadres, mais notre application semble assez lente à ce point. Je vais ensuite essayer quelques warm-up des stratégies pour le faire paraître mieux

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X