Nous travaillons sur une application qui utilise les nouvelles fonctions cloud de Firebase. Ce qui se passe actuellement, c'est qu'une transaction est placée dans le nœud de file d'attente. Et puis la fonction supprime ce nœud et le place dans le nœud correct. Cela a été mis en œuvre en raison de la possibilité de travailler hors ligne.
Notre problème actuel est la vitesse de la fonction. La fonction elle-même prend environ 400 ms, donc ce n'est pas grave. Mais parfois, les fonctions prennent un temps très long (environ 8 secondes), alors que l'entrée a déjà été ajoutée à la file d'attente.
Nous soupçonnons que le serveur met du temps à démarrer, car lorsque nous recommençons l'action après la première. Cela prend beaucoup moins de temps.
Existe-t-il un moyen de résoudre ce problème? Ici, j'ai ajouté le code de notre fonction. Nous pensons qu'il n'y a rien de mal à cela, mais nous l'avons ajouté 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;
}
la source
Réponses:
firebaser ici
Il semble que vous rencontriez un soi-disant démarrage à froid de la fonction.
Lorsque votre fonction n'a pas été exécutée depuis un certain temps, Cloud Functions la met dans un mode qui utilise moins de ressources. Ensuite, lorsque vous appuyez à nouveau sur la fonction, elle restaure l'environnement à partir de ce mode. Le temps nécessaire à la restauration se compose d'un coût fixe (par exemple, restaurer le conteneur) et d'un coût variable en partie (par exemple, si vous utilisez beaucoup de modules de nœuds, cela peut prendre plus de temps).
Nous surveillons en permanence les performances de ces opérations pour garantir la meilleure combinaison entre l'expérience des développeurs et l'utilisation des ressources. Attendez-vous donc à ce que ces temps s'améliorent avec le temps.
La bonne nouvelle est que vous ne devriez en faire l'expérience que pendant le développement. Une fois que vos fonctions sont fréquemment déclenchées en production, il y a de fortes chances qu'elles ne recommencent presque jamais à démarrer à froid.
la source
Mise à jour mai 2020 Merci pour le commentaire de maganap - dans Node 10+
FUNCTION_NAME
est remplacé parK_SERVICE
(FUNCTION_TARGET
est la fonction elle-même, pas son nom, le remplacementENTRY_POINT
). Les exemples de code ci-dessous ont été mis à jour ci-dessous.Plus d'informations sur https://cloud.google.com/functions/docs/migrating/nodejs-runtimes#nodejs-10-changes
Mise à jour - il semble que beaucoup de ces problèmes peuvent être résolus en utilisant la variable cachée
process.env.FUNCTION_NAME
comme on le voit ici: https://github.com/firebase/functions-samples/issues/170#issuecomment-323375462Mettre à jour avec le code - Par exemple, si vous disposez du fichier d'index suivant:
Ensuite, tous vos fichiers seront chargés, et toutes les exigences de ces fichiers seront également chargées, ce qui entraînera beaucoup de surcharge et polluera votre portée globale pour toutes vos fonctions.
Au lieu de cela, séparez vos inclusions en:
Cela ne chargera le (s) fichier (s) requis que lorsque cette fonction sera spécifiquement appelée; vous permettant de garder votre lunette globale beaucoup plus propre, ce qui devrait entraîner des démarrages à froid plus rapides.
Cela devrait permettre une solution beaucoup plus soignée que ce que j'ai fait ci-dessous (bien que l'explication ci-dessous soit toujours valable).
Réponse originale
Il semble que le fait d'exiger des fichiers et une initialisation générale dans la portée globale soit une cause énorme de ralentissement lors du démarrage à froid.
Au fur et à mesure qu'un projet obtient de plus en plus de fonctions, la portée globale est de plus en plus polluée, ce qui aggrave le problème - surtout si vous étendez vos fonctions dans des fichiers séparés (par exemple en utilisant
Object.assign(exports, require('./more-functions.js'));
dans votreindex.js
.J'ai réussi à voir d'énormes gains dans les performances de démarrage à froid en déplaçant tous mes besoins dans une méthode init comme ci-dessous, puis en l'appelant comme la première ligne de toute définition de fonction pour ce fichier. Par exemple:
J'ai vu des améliorations d'environ 7-8 à 2-3s lors de l'application de cette technique à un projet avec ~ 30 fonctions sur 8 fichiers. Cela semble également obliger les fonctions à être démarrées à froid moins souvent (probablement en raison d'une moindre utilisation de la mémoire?)
Malheureusement, cela rend encore les fonctions HTTP à peine utilisables pour une utilisation en production face à l'utilisateur.
En espérant que l'équipe Firebase ait des plans à l'avenir pour permettre une définition appropriée des fonctions afin que seuls les modules pertinents aient jamais besoin d'être chargés pour chaque fonction.
la source
process.env.FUNCTION_NAME
et de l'utiliser pour inclure conditionnellement les fichiers requis pour cette fonction. Le commentaire sur github.com/firebase/functions-samples/issues/… donne une très bonne description de ce fonctionnement! Cela garantit que la portée globale n'est pas polluée par des méthodes et inclut des fonctions non pertinentes.FUNCTIONS_NAME
n'est valide qu'avec les nœuds 6 et 8, comme expliqué ici: cloud.google.com/functions/docs/… . Node 10 devrait utiliserFUNCTION_TARGET
K_SERVICE
selon doco sur cloud.google.com/functions/docs/migrating / ... - J'ai mis à jour ma réponse.Je suis confronté à des problèmes similaires avec les fonctions cloud de Firestore. Le plus gros est la performance. Surtout en cas de démarrage précoce, lorsque vous ne pouvez pas permettre à vos premiers clients de voir des applications «lentes». Une simple fonction de génération de documentation pour par exemple donne ceci:
- L'exécution de la fonction a pris 9522 ms, terminée avec le code d'état: 200
Ensuite: j'avais une page de conditions générales simple. Avec les fonctions cloud, l'exécution due au démarrage à froid prendrait de 10 à 15 secondes, même parfois. Je l'ai ensuite déplacé vers une application node.js, hébergée sur le conteneur appengine. Le temps est descendu à 2-3 secondes.
J'ai comparé de nombreuses fonctionnalités de mongodb avec Firestore et parfois je me demande aussi si, au cours de cette première phase de mon produit, je devrais également passer à une autre base de données. Le plus gros avantage que j'avais dans Firestore était la fonctionnalité de déclenchement onCreate, onUpdate des objets de document.
https://db-engines.com/en/system/Google+Cloud+Firestore%3BMongoDB
Fondamentalement, s'il y a des parties statiques de votre site qui peuvent être déchargées vers l'environnement appengine, ce n'est peut-être pas une mauvaise idée.
la source
J'ai également fait ces choses, ce qui améliore les performances une fois les fonctions réchauffées, mais le démarrage à froid me tue. L'un des autres problèmes que j'ai rencontrés concerne les cors, car il faut deux voyages vers les fonctions cloud pour faire le travail. Je suis sûr que je peux résoudre ce problème.
Lorsque vous avez une application dans sa phase initiale (démo) lorsqu'elle n'est pas utilisée fréquemment, les performances ne seront pas excellentes. C'est quelque chose qui doit être pris en compte, car les premiers utilisateurs avec un produit précoce doivent être sous leur meilleur jour devant les clients / investisseurs potentiels. Nous avons adoré la technologie, nous avons donc migré à partir d'anciens frameworks éprouvés, mais notre application semble assez lente à ce stade. Je vais ensuite essayer quelques stratégies d'échauffement pour le rendre meilleur
la source
UPDATE / EDIT: nouvelle syntaxe et mises à jour à venir MAI2020
Je viens de publier un package appelé
better-firebase-functions
, il recherche automatiquement votre répertoire de fonctions et imbrique correctement toutes les fonctions trouvées dans votre objet d'exportations, tout en isolant les fonctions les unes des autres pour améliorer les performances de démarrage à froid.Si vous chargez et mettez en cache uniquement les dépendances dont vous avez besoin pour chaque fonction dans la portée du module, vous constaterez que c'est le moyen le plus simple et le plus simple de garder vos fonctions de manière optimale efficaces sur un projet à croissance rapide.
la source