Estamos trabajando en una aplicación que utiliza las nuevas funciones en la nube de Firebase. Lo que está sucediendo actualmente es que una transacción se coloca en el nodo de la cola. Y luego la función elimina ese nodo y lo coloca en el nodo correcto. Esto se ha implementado debido a la capacidad de trabajar sin conexión.
Nuestro problema actual es la velocidad de la función. La función en sí misma tarda unos 400 ms, así que está bien. Pero a veces las funciones toman mucho tiempo (alrededor de 8 segundos), mientras que la entrada ya se agregó a la cola.
Sospechamos que el servidor tarda en iniciarse, porque cuando hacemos la acción una vez más después del primero. Lleva mucho menos tiempo.
Hay alguna manera de arreglar este problema? Aquí abajo agregué el código de nuestra función. Sospechamos que no tiene nada de malo, pero lo agregamos por si acaso.
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;
}
fuente
Respuestas:
bombero aquí
Parece que está experimentando un llamado arranque en frío de la función.
Cuando su función no se ha ejecutado en algún momento, Cloud Functions la pone en un modo que utiliza menos recursos. Luego, cuando vuelve a presionar la función, restaura el entorno desde este modo. El tiempo que se tarda en restaurar consiste en un costo fijo (por ejemplo, restaurar el contenedor) y un costo variable parcial (por ejemplo, si usa muchos módulos de nodo, puede llevar más tiempo).
Continuamente estamos monitoreando el desempeño de estas operaciones para asegurar la mejor combinación entre la experiencia del desarrollador y el uso de recursos. Espere que estos tiempos mejoren con el tiempo.
La buena noticia es que solo debe experimentar esto durante el desarrollo. Una vez que sus funciones se activan con frecuencia en la producción, es probable que casi nunca vuelvan a comenzar en frío.
fuente
Actualización de mayo de 2020 Gracias por el comentario de maganap: en el Nodo 10+
FUNCTION_NAME
se reemplaza porK_SERVICE
(FUNCTION_TARGET
es la función en sí, no su nombre, el reemplazoENTRY_POINT
). Los ejemplos de código a continuación se han actualizado a continuación.Más información en https://cloud.google.com/functions/docs/migrating/nodejs-runtimes#nodejs-10-changes
Actualización : parece que muchos de estos problemas se pueden resolver utilizando la variable oculta
process.env.FUNCTION_NAME
como se ve aquí: https://github.com/firebase/functions-samples/issues/170#issuecomment-323375462Actualizar con código : por ejemplo, si tiene el siguiente archivo de índice:
Luego, todos sus archivos se cargarán y todos los requisitos de esos archivos también se cargarán, lo que generará una gran sobrecarga y contaminará su alcance global para todas sus funciones.
En lugar de separar su incluye como:
Esto solo cargará los archivos requeridos cuando se llama específicamente a esa función; lo que le permite mantener su alcance global mucho más limpio, lo que debería resultar en botas de frío más rápidas.
Esto debería permitir una solución mucho más ordenada que la que he hecho a continuación (aunque la explicación a continuación sigue siendo válida).
Respuesta original
Parece que requerir archivos e inicialización general en el ámbito global es una gran causa de desaceleración durante el arranque en frío.
A medida que un proyecto obtiene más funciones, el alcance global se contamina cada vez más, lo que empeora el problema, especialmente si coloca sus funciones en archivos separados (como al usar
Object.assign(exports, require('./more-functions.js'));
en suindex.js
.Me las arreglé para ver grandes ganancias en el rendimiento de arranque en frío moviendo todos mis requisitos a un método init como se muestra a continuación y luego llamándolo como la primera línea dentro de cualquier definición de función para ese archivo. P.ej:
He visto mejoras de aproximadamente 7-8 segundos a 2-3 segundos al aplicar esta técnica a un proyecto con ~ 30 funciones en 8 archivos. Esto también parece causar que las funciones deban iniciarse en frío con menos frecuencia (¿presumiblemente debido a un menor uso de memoria?)
Desafortunadamente, esto todavía hace que las funciones HTTP sean apenas utilizables para el uso de producción orientado al usuario.
Esperando que el equipo de Firebase tenga algunos planes en el futuro para permitir el alcance adecuado de las funciones, de modo que solo los módulos relevantes necesiten cargarse para cada función.
fuente
process.env.FUNCTION_NAME
y usarla para incluir condicionalmente los archivos necesarios para esa función. ¡El comentario en github.com/firebase/functions-samples/issues/… da una muy buena descripción de este trabajo! Asegura que el alcance global no esté contaminado con métodos e incluye funciones irrelevantes.FUNCTIONS_NAME
solo es válido con los nodos 6 y 8, como se explica aquí: cloud.google.com/functions/docs/… . Nodo 10 debería usarFUNCTION_TARGET
K_SERVICE
según doco en cloud.google.com/functions/docs/migrating/… - He actualizado mi respuesta.Estoy enfrentando problemas similares con las funciones de nube de firestore. El más grande es el rendimiento. Especialmente en el caso de nuevas empresas en etapa inicial, cuando no puede permitirse que sus primeros clientes vean aplicaciones "lentas". Una función simple de generación de documentación, por ejemplo, proporciona esto:
- La ejecución de la función tomó 9522 ms, finalizó con el código de estado: 200
Luego: tenía una página de términos y condiciones sencilla. Con las funciones en la nube, la ejecución debido al arranque en frío tardaría entre 10 y 15 segundos, incluso a veces. Luego lo moví a una aplicación node.js, alojada en un contenedor de appengine. El tiempo se ha reducido a 2-3 segundos.
He estado comparando muchas de las características de mongodb con firestore y, a veces, también me pregunto si durante esta fase temprana de mi producto también debería pasar a una base de datos diferente. El mayor consejo que tuve en firestore fue la funcionalidad de activación onCreate, onUpdate de objetos de documento.
https://db-engines.com/en/system/Google+Cloud+Firestore%3BMongoDB
Básicamente, si hay partes estáticas de su sitio que pueden descargarse en un entorno de motor, quizás no sea una mala idea.
fuente
También he hecho estas cosas, lo que mejora el rendimiento una vez que se calientan las funciones, pero el arranque en frío me está matando. Uno de los otros problemas que he encontrado es con cors, porque se necesitan dos viajes a las funciones de la nube para hacer el trabajo. Sin embargo, estoy seguro de que puedo arreglar eso.
Cuando tiene una aplicación en su fase inicial (demo) cuando no se usa con frecuencia, el rendimiento no será excelente. Esto es algo que debe considerarse, ya que los primeros usuarios con productos tempranos deben verse lo mejor posible frente a clientes / inversores potenciales. Nos encantó la tecnología, por lo que migramos de marcos de trabajo antiguos y probados, pero nuestra aplicación parece bastante lenta en este momento. A continuación, intentaré algunas estrategias de calentamiento para que se vea mejor
fuente
ACTUALIZACIÓN / EDICIÓN: nueva sintaxis y actualizaciones próximamente MAYO2020
Acabo de publicar un paquete llamado
better-firebase-functions
, busca automáticamente en su directorio de funciones y anida correctamente todas las funciones encontradas en su objeto de exportaciones, al tiempo que aísla las funciones entre sí para mejorar el rendimiento del arranque en frío.Si carga de manera diferida y almacena en caché solo las dependencias que necesita para cada función dentro del alcance del módulo, encontrará que es la forma más simple y fácil de mantener sus funciones de manera óptima y eficiente en un proyecto de rápido crecimiento.
fuente