Las funciones de la nube de Firebase son muy lentas

Estamos trabajando en una aplicación que usa las nuevas funciones de nube de firebase. Lo que ocurre actualmente es que una transacción se coloca en el nodo de 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 fuera de línea.

Nuestro problema actual es la velocidad de la función. La función en sí misma toma alrededor de 400ms, 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 tiempo en arrancar, porque cuando hacemos la acción una vez más después de la primera. 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  { // 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; } 

firebaser aquí

Parece que estás experimentando un llamado comienzo en frío de la función.

Cuando su función no se ha ejecutado en algún momento, Cloud Functions la coloca 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 en parte (por ejemplo, si usa muchos módulos de nodo, puede llevar más tiempo).

Supervisamos continuamente el rendimiento de estas operaciones para garantizar la mejor combinación entre la experiencia del desarrollador y el uso de recursos. Así que espere que estos tiempos mejoren con el tiempo.

La buena noticia es que solo debes experimentar esto durante el desarrollo. Una vez que sus funciones se desencadenan con frecuencia en la producción, es probable que apenas vuelvan a tener un inicio en frío.

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-323375462

Actualizar con código : por ejemplo, si tiene el siguiente archivo de índice:

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

Luego, se cargarán todos sus archivos y se cargarán todos los requisitos de esos archivos, lo que generará una gran sobrecarga y contaminará su scope global para todas sus funciones.

En cambio, separa tus incluye como:

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

Esto solo cargará los archivos necesarios cuando esa función se llame específicamente; permitiéndole mantener su scope global mucho más limpio, lo que debería resultar en botas frías 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 todavía se cumple).


Respuesta original

Parece que requerir archivos y la 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 scope global se contamina cada vez más empeorando el problema, especialmente si amplía sus funciones a archivos separados (por ejemplo, al utilizar Object.assign(exports, require('./more-functions.js')); en su index.js .

Logré ver grandes ganancias en el rendimiento de arranque en frío moviendo todas mis necesidades en un método init como el siguiente y luego llamándolo como la primera línea dentro de cualquier definición de función para ese archivo. P.ej:

 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 comstacktion here too initialised = true; } 

He visto mejoras de aproximadamente 7-8 a 2-3 s cuando se aplica esta técnica a un proyecto con ~ 30 funciones en 8 archivos. Esto también parece causar que las funciones necesiten ser iniciadas en frío con menos frecuencia (presumiblemente debido a un menor uso de memoria).

Desafortunadamente, esto todavía hace que las funciones HTTP apenas sean utilizables para el uso de producción orientado al usuario.

Esperando que el equipo de Firebase tenga planes en el futuro para permitir un scope adecuado de las funciones, de modo que solo los módulos relevantes deban cargarse para cada función.

Estoy enfrentando problemas similares con las funciones de la nube Firerestre. El más grande es el rendimiento. Especialmente en el caso de startups de etapa temprana, cuando no puede permitirse que sus primeros clientes vean aplicaciones “inactivas”. Una función simple de generación de documentación para, por ejemplo, da esto:

– La ejecución de la función tomó 9522 ms, terminó con el código de estado: 200

Luego: tuve una página de términos y condiciones claros. Con las funciones en la nube, la ejecución debido al inicio en frío tomaría entre 10 y 15 segundos incluso a veces. Luego lo moví a la aplicación node.js, alojada en el contenedor appengine. El tiempo ha bajado a 2-3 segundos.

He estado comparando muchas de las características de mongodb con Firerestre y, a veces, también me pregunto si durante esta primera fase de mi producto también debería pasar a una base de datos diferente. El adv más grande que tuve en Firerestre fue la funcionalidad de activación onCreate, onUpdate of document objects.

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

Básicamente, si hay partes estáticas de su sitio que pueden descargarse al entorno appengine, quizás no sea una mala idea.

Otro problema es el plan gratuito (Spark).

chispa

Una vez que cambio al plan pago (Blaze en mi caso), mis funciones comienzan a funcionar rápidamente.

resplandor

También hice estas cosas, lo que mejora el rendimiento una vez que las funciones se calientan, 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. Estoy seguro de que puedo arreglar eso, sin embargo.

Cuando tienes una aplicación en su fase inicial (demo) cuando no se usa con frecuencia, el rendimiento no será muy bueno. Esto es algo que debe tenerse en cuenta, ya que los primeros usuarios con productos tempranos deben verse lo mejor posible frente a clientes / inversores potenciales. Nos encantó la tecnología, así que migramos de frameworks probados y verdaderos, pero nuestra aplicación parece bastante lenta en este punto. Voy a probar algunas estrategias de calentamiento para que se vea mejor

    Intereting Posts